master
Mark MacKay 2021-02-13 08:27:17 -06:00
parent f0421482a3
commit 42a48249c1
20 changed files with 279 additions and 54 deletions

View File

@ -62,6 +62,11 @@
border: solid #ccc 1px;
}
#tool_fill .color_block svg {
width: 24px;
height: 24px;
}
.touch #tool_eyedropper {
margin-top: 6px;
}

View File

@ -8,7 +8,7 @@
padding: 5px 0;
margin: 0px;
display: none;
font: 12px/15px 'Lucida Sans', 'Lucida Grande', Helvetica, Verdana, sans-serif;
font: 14px/18px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
border-radius: 5px;
-moz-border-radius: 5px;
-moz-box-shadow: 2px 5px 10px rgba(0,0,0,.3);

View File

@ -134,16 +134,15 @@
cursor: -moz-grabbing;
}
.draginput span {
font: 11px/130% sans-serif;
.draginput span {
font: 12px/130% sans-serif;
color: #ccc;
display: block;
position: absolute;
top: 5px;
left: 5px;
text-align: left;
white-space: nowrap;
}
.draginput.error {

View File

@ -2,7 +2,7 @@
body {
background: #3f3f3c;
font: 13px/120% 'Lucida Sans', 'Lucida Grande', 'Lucida Sans Unicode', sans-serif;
font: 14px/120% -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
@ -363,7 +363,11 @@ box-shadow: inset 0 3px 10px rgba(255, 255, 255, 0.1),
z-index: 4;
}
#svg_editor.wireframe #workarea #svgcontent * {
#svgcontent {
overflow: hidden;
}
.wireframe #svgcontent * {
fill: none;
stroke: #000;
stroke-width: 1px;
@ -375,16 +379,16 @@ box-shadow: inset 0 3px 10px rgba(255, 255, 255, 0.1),
filter: none;
}
#svg_editor.wireframe #workarea #svgcontent text {
.wireframe #svgcontent text {
fill: #000;
stroke: none;
}
#svg_editor.wireframe #workarea #canvasBackground > rect {
#canvasBackground > rect {
fill: #FFF !important;
}
#workarea #canvasBackground > rect {
#canvasBackground > rect {
stroke: transparent !important;
}
@ -466,16 +470,15 @@ box-shadow: inset 0 3px 10px rgba(255, 255, 255, 0.1),
overflow: hidden;
}
#tool_open input,
#tool_import input,
#tool_import_bitmap input {
input[type=file] {
-webkit-appearance: none;
position: absolute;
opacity: 0;
font-size: 10em;
top: -5px;
right: -5px;
margin: 0;
cursor: pointer; /* Sadly doesn't appear to have an effect */
background: red;
z-index: 10;
height: 30px;
width: 100%;
transform: translate(-100%, 0);
}
.disabled {
@ -623,7 +626,7 @@ box-shadow: inset 0 3px 10px rgba(255, 255, 255, 0.1),
width: 100%;
height: 100%;
line-height: 140%;
font-family: 'Lucida Sans', 'Lucida Grande', 'Lucida Sans Unicode', sans-serif;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}

View File

@ -70,8 +70,14 @@
<div class="menu_title">File</div>
<div class="menu_list" id="file_menu">
<div id="tool_clear" class="menu_item">New Document</div>
<div id="tool_open" class="menu_item" style="display: none;"><div id="fileinputs"></div>Open SVG...</div>
<div id="tool_import" class="menu_item" style="display: none;"><div id="fileinputs_import"></div>Import Image...</div>
<div id="tool_open" class="menu_item">
Open SVG...</div>
<input type="file" accept="image/svg+xml" />
<div id="tool_import" class="menu_item">
Place Image... <span class="shortcut">⌘K</span></div>
<input type="file" accept="image/*" />
<div id="tool_save" class="menu_item">Save Image... <span class="shortcut">⌘S</span></div>
<div id="tool_export" class="menu_item">Export as PNG</div>
</div>
@ -485,11 +491,11 @@
<div class="toolset" data-title="Change stroke">
<label>
<input id="stroke_width" size="2" value="1" data-attr="stroke-width" min="0" max="99" step="1" />
<span class="icon_label">Stroke Width</span>
<span class="icon_label">Width</span>
</label>
</div>
<div class="stroke_tool draginput">
<span>Stroke Dash</span>
<span>Dash</span>
<select id="stroke_style" data-title="Change stroke dash style">
<option selected="selected" value="none"></option>
<option value="2,2">···</option>
@ -700,10 +706,12 @@
<script type="text/javascript" src="js/Toolbar.js"></script>
<script type="text/javascript" src="js/Menu.js"></script>
<script type="text/javascript" src="js/Keyboard.js"></script>
<script type="text/javascript" src="js/Import.js"></script>
<script type="text/javascript" src="js/PaintBox.js"></script>
<script type="text/javascript" src="js/Palette.js"></script>
<script type="text/javascript" src="js/Zoom.js"></script>
<script type="text/javascript" src="js/Modal.js"></script>
<script type="text/javascript" src="js/ContextMenu.js"></script>
<script type="text/javascript" src="js/Shapelib.js"></script>
<script type="text/javascript" src="js/fonts.js"></script>
<script type="text/javascript" src="js/dialog.js"></script>

View File

@ -3,6 +3,13 @@ MD.Canvas = function(){
const el = document.getElementById("svgcanvas");
var workarea = document.getElementById("workarea");
workarea.addEventListener("mouseup", function(){
const mode = svgCanvas.getMode();
// todo why?
if (mode !== "textedit" && mode !== "path") state.set("canvasMode", mode);
workarea.className = mode;
})
$('#resolution').change(function(){
var w = $('#canvas_width')[0];
var h = $('#canvas_height')[0];

56
src/js/ContextMenu.js Normal file
View File

@ -0,0 +1,56 @@
MD.ContextMenu = function(){
$("#workarea").contextMenu({
menu: 'cmenu_canvas',
inSpeed: 0
},
function(action, el, pos) {
switch ( action ) {
case 'delete':
editor.deleteSelected();
break;
case 'cut':
editor.cutSelected();
break;
case 'copy':
editor.copySelected();
break;
case 'paste':
editor.pasteSelected();
break;
case 'paste_in_place':
svgCanvas.pasteElements('in_place');
break;
case 'group':
editor.groupSelected();
break;
case 'ungroup':
editor.ungroupSelected();
break;
case 'move_front':
editor.moveToTopSelected();
break;
case 'move_up':
editor.moveUpSelected();
break;
case 'move_down':
editor.moveDownSelected();
break;
case 'move_back':
editor.moveToBottomSelected();
break;
default:
if(svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)){
svgedit.contextmenu.getCustomHandler(action).call();
}
break;
}
});
$('.contextMenu li').mousedown(function(ev) {
ev.preventDefault();
})
$('#cmenu_canvas li').disableContextMenu();
$("#cmenu_canvas").enableContextMenuItems('#delete,#cut,#copy');
}

138
src/js/Import.js Normal file
View File

@ -0,0 +1,138 @@
MD.Import = function(){
const workarea = document.getElementById("workarea");
const $importInput = $('#tool_import + input');
const $openInput = $('#tool_open + input');
$('#tool_import').on("click", place);
$('#tool_open').on("click", open);
$importInput.change(importImage);
$openInput.change(openImage);
function importImage(e){
if (!window.FileReader) return;
//e.stopPropagation();
//e.preventDefault();
workarea.removeAttribute("style");
$('#main_menu').hide();
var file = null;
if (e.type == "drop") file = e.dataTransfer.files[0]
else file = this.files[0];
if (!file) return $.alert("File not found");
if (file.type.indexOf("image") === -1) return $.alert("File is not image");
//svg handing
if(file.type.indexOf("svg") != -1) {
var reader = new FileReader();
reader.onloadend = function(e) {
svgCanvas.importSvgString(e.target.result, true);
svgCanvas.ungroupSelectedElement()
svgCanvas.ungroupSelectedElement()
svgCanvas.groupSelectedElements()
svgCanvas.alignSelectedElements("m", "page")
svgCanvas.alignSelectedElements("c", "page")
};
reader.readAsText(file);
}
//image handling
else {
var reader = new FileReader();
reader.onloadend = function(e) {
// lets insert the new image until we know its dimensions
insertNewImage = function(img_width, img_height){
var newImage = svgCanvas.addSvgElementFromJson({
"element": "image",
"attr": {
"x": 0,
"y": 0,
"width": img_width,
"height": img_height,
"id": svgCanvas.getNextId(),
"style": "pointer-events:inherit"
}
});
svgCanvas.setHref(newImage, e.target.result);
svgCanvas.selectOnly([newImage])
svgCanvas.alignSelectedElements("m", "page")
svgCanvas.alignSelectedElements("c", "page")
updateContextPanel();
}
// put a placeholder img so we know the default dimensions
var img_width = 100;
var img_height = 100;
var img = new Image()
img.src = e.target.result
document.body.appendChild(img);
img.onload = function() {
img_width = img.offsetWidth
img_height = img.offsetHeight
insertNewImage(img_width, img_height);
document.body.removeChild(img);
}
};
reader.readAsDataURL(file)
}
}
function loadSvgString(str, callback) {
var success = svgCanvas.setSvgString(str) !== false;
callback = callback || $.noop;
if(success) {
callback(true);
} else {
$.alert("Error: Unable to load SVG data", function() {
callback(false);
});
}
}
function openImage(e){
const f = this;
if(f.files.length==1) {
svgCanvas.clear();
var reader = new FileReader();
reader.onloadend = function(e) {
loadSvgString(e.target.result);
editor.canvas.update();
};
reader.readAsText(f.files[0]);
}
}
function onDragEnter(e) {
e.stopPropagation();
e.preventDefault();
workarea.style.transform = "scale(1.1)";
}
function onDragOver(e) {
e.stopPropagation();
e.preventDefault();
}
function onDragLeave(e) {
workarea.removeAttribute("style");
e.stopPropagation();
e.preventDefault();
}
function place(){
$importInput.trigger("click");
}
function open(){
$openInput.trigger("click");
}
workarea.addEventListener('dragenter', onDragEnter, false);
workarea.addEventListener('dragover', onDragOver, false);
workarea.addEventListener('dragleave', onDragLeave, false);
workarea.addEventListener('drop', importImage, false);
this.place = place;
this.open = open;
}

View File

@ -29,6 +29,8 @@ MD.Keyboard = function(){
"cmd_b": ()=> editor.text.setBold(),
"cmd_i": ()=> editor.text.setItalic(),
"cmd_g": ()=> editor.groupSelected(),
"cmd_o": ()=> editor.import.open(),
"cmd_k": ()=> editor.import.place(),
"cmd_shift_g": ()=> editor.ungroupSelected(),
"backspace": () => editor.deleteSelected(),
"ctrl_arrowleft": () => editor.rotateSelected(0,1),
@ -54,6 +56,7 @@ MD.Keyboard = function(){
"shift_arrowup": () => editor.moveSelected(0, state.get("canvasSnapStep") * -1),
"shift_arrowdown": () => editor.moveSelected(0, state.get("canvasSnapStep") * 1),
"escape": () => editor.escapeMode(),
"enter": () => editor.escapeMode(),
" ": ()=> editor.pan.startPan(),
};

View File

@ -5,6 +5,7 @@ MD.Menu = function(){
$('#tool_move_up').on("click", editor.moveUpSelected);
$('#tool_move_bottom').on("click", editor.moveToBottomSelected);
$('#tool_move_down').on("click", editor.moveDownSelected);
$('#tool_topath').on("click", editor.convertToPath);
// top dropdown menus
$('.menu_title')

View File

@ -24,7 +24,7 @@ MD.PaintBox = function(container, type){
function(p) {
paint = new $.jGraduate.Paint(p);
editor.paintBox[picker].setPaint(paint);
editor.paintBox[picker].setPaint(paint, true);
svgCanvas.setPaint(picker, paint);
$('#color_picker').hide();
@ -88,7 +88,7 @@ MD.PaintBox = function(container, type){
var cur = {color: "fff", opacity: 1}
if (type === "stroke") cur = {color: 'fff', opacity: 1};
if (type === "stroke") cur = {color: '000', opacity: 1};
if (type === "fill") cur = {color: 'fff', opacity: 1};
if (type === "canvas") cur = {color: 'fff', opacity: 1};
@ -127,7 +127,7 @@ MD.PaintBox = function(container, type){
this.rect.setAttribute('fill', fillAttr);
this.rect.setAttribute('opacity', opac);
if (this.type == "canvas") {
if (this.type === "canvas") {
//recache background in case it changed
var background = document.getElementById("canvas_background");
if (background) {
@ -140,8 +140,8 @@ MD.PaintBox = function(container, type){
}
else createBackground(fillAttr)
}
if(apply) {
if(true) {
svgCanvas.setColor(this.type, fillAttr, true);
svgCanvas.setPaintOpacity(this.type, opac, true);
}

View File

@ -105,6 +105,7 @@ MD.Panel = function(){
const isNode = svgCanvas.pathActions.getNodePoint()
// If element has just been deleted, consider it null
if(!elem || !elem.parentNode) elem = null;
elem = null;
const multiselected = elems.length > 1;

View File

@ -52,7 +52,8 @@ MD.Text = function(){
.keyup(function(e){
e.stopPropagation();
if (e.key === "Escape") {
return svgCanvas.textActions.toSelectMode()
svgCanvas.textActions.toSelectMode()
return editor.escapeMode();
}
svgCanvas.setTextContent(this.value);
});

View File

@ -10,6 +10,7 @@ MD.Toolbar = function(){
function setMode(mode) {
$(".tool_button").removeClass("current");
$("#tool_" + mode).addClass("current");
$("#workarea").attr("class", mode);
svgCanvas.setMode(mode);
}

View File

@ -4,6 +4,7 @@ MD.Editor = function(){
const serializer = new XMLSerializer();
const _self = this;
const workarea = document.getElementById("workarea");
_self.selected = [];
function save(){
@ -36,7 +37,6 @@ MD.Editor = function(){
function pasteSelected(){
_self.menu.flash($('#edit_menu'));
const workarea = document.getElementById("workarea");
var zoom = svgCanvas.getZoom();
var x = (workarea.scrollLeft + workarea.offsetWidth/2)/zoom - svgCanvas.contentW;
var y = (workarea.scrollTop + workarea.offsetHeight/2)/zoom - svgCanvas.contentH;
@ -102,13 +102,13 @@ MD.Editor = function(){
};
function escapeMode(){
svgCanvas.setMode("select")
state.set("canvasMode", "select");
state.set("canvasContent", svgCanvas.getSvgString())
}
// called when we've selected a different element
function selectedChanged(window,elems) {
const mode = svgCanvas.getMode();
if(mode === "select") editor.toolbar.setMode("select");
_self.selected = elems.filter(Boolean);
editor.panel.updateContextPanel(_self.selected);
};
@ -144,7 +144,7 @@ MD.Editor = function(){
const mode = svgCanvas.getMode();
// if the element changed was the svg, then it could be a resolution change
if (elems[0].tagName === "svg") editor.canvas.update();
if (elems[0].tagName === "svg") return editor.canvas.update();
editor.panel.updateContextPanel(elems);
@ -157,6 +157,8 @@ MD.Editor = function(){
svgCanvas.runExtensions("elementChanged", {
elems: elems
});
state.set("canvasContent", svgCanvas.getSvgString())
}
function changeAttribute(attr, value, completed) {
@ -169,9 +171,6 @@ MD.Editor = function(){
}
function elementTransition(window, elems){
// Call when part of element is in process of changing, generally
// on mousemove actions like rotate, move, etc.
var elementTransition = function(window,elems) {
var mode = svgCanvas.getMode();
var elem = elems[0];
@ -179,20 +178,13 @@ MD.Editor = function(){
const multiselected = (elems.length >= 2 && elems[1] != null) ? elems : null;
// Only updating fields for single elements for now
if(!multiselected) {
switch ( mode ) {
case "rotate":
var ang = svgCanvas.getRotationAngle(elem);
$('#angle').val(Math.round(ang));
rotateCursor(ang);
$('#tool_reorient').toggleClass('disabled', ang == 0);
break;
}
if(!multiselected && mode === "rotate") {
var rotate_string = 'rotate('+ svgCanvas.getRotationAngle(elem) + 'deg)';
$('#tool_angle_indicator').css("transform", rotate_string);
}
svgCanvas.runExtensions("elementTransition", {
elems: elems
});
};
}
function moveSelected(dx,dy) {
@ -288,6 +280,17 @@ MD.Editor = function(){
}
}
function convertToPath(){
if (!_self.selected.length) return;
svgCanvas.convertToPath();
var elems = svgCanvas.getSelectedElems();
svgCanvas.selectorManager.requestSelector(elems[0]).reset(elems[0])
svgCanvas.selectorManager.requestSelector(elems[0]).selectorRect.setAttribute("display", "none");
svgCanvas.setMode("pathedit")
svgCanvas.pathActions.toEditMode(elems[0]);
svgCanvas.clearSelection();
editor.panel.updateContextPanel();
}
this.selectedChanged = selectedChanged;
this.elementChanged = elementChanged;
@ -316,4 +319,5 @@ MD.Editor = function(){
this.toggleWireframe = toggleWireframe;
this.groupSelected = groupSelected;
this.ungroupSelected = ungroupSelected;
this.convertToPath = convertToPath;
}

View File

@ -75,7 +75,7 @@ MD.Eyedropper = function(S) {
icon: "extensions/eyedropper.png",
events: {
"click": function() {
svgCanvas.setMode("eyedropper");
state.set("canvasMode", "eyedropper");
}
}
}],

View File

@ -40,7 +40,7 @@ MD.Palette = function(){
if (evt.type === "mousedown" || evt.type === "touchstart") picking = true;
if (!picking) return;
var isStroke = toolStroke.classList.contains('active');
var isStroke = toolStroke.classList.contains('active') || evt.shiftKey;
var picker = isStroke ? "stroke" : "fill";
var color = this.getAttribute('data-rgb');
var paint = null;

View File

@ -17,6 +17,8 @@ editor.palette = new MD.Palette();
editor.keyboard = new MD.Keyboard();
editor.pan = new MD.Pan();
editor.modal = new MD.Modal();
editor.import = new MD.Import();
editor.contextMenu = new MD.ContextMenu();
// bind the selected event to our function that handles updates to the UI
svgCanvas.bind("selected", editor.selectedChanged);
@ -34,5 +36,6 @@ const state = new State();
state.set("canvasId", t("Untitled"));
state.set("canvasMode", state.get("canvasMode"));
state.set("canvasSize", state.get("canvasSize"));
svgCanvas.setSvgString(state.get("canvasContent"))
editor.rulers.update();

View File

@ -36,7 +36,7 @@ function State(){
}
Object.keys(localStorage).forEach(key => localStorage.removeItem(key));
write.reset();
window.location.reload();
}
// INNER UTILS

View File

@ -88,7 +88,6 @@ var clearSvgContentElement = canvas.clearSvgContentElement = function() {
height: dimensions[1],
x: dimensions[0],
y: dimensions[1],
overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden',
xmlns: svgns,
"xmlns:se": se_ns,
"xmlns:xlink": xlinkns
@ -3078,7 +3077,6 @@ var getMouseTarget = this.getMouseTarget = function(evt) {
{
canvas.addClones = false;
window.removeEventListener("keyup", canvas.removeClones)
workarea.className = state.get("canvasMode");
selectedElements = selectedElements.filter(Boolean)
if(evt.button === 2) return;
var tempJustSelected = justSelected;
@ -5880,7 +5878,6 @@ this.setSvgString = function(xmlString) {
var attrs = {
id: 'svgcontent',
overflow: curConfig.show_outside_canvas?'visible':'hidden'
};
var percs = false;
@ -6835,10 +6832,8 @@ this.getMode = function() {
// Parameters:
// name - String with the new mode to change to
this.setMode = function(name) {
pathActions.clear();
textActions.clear();
$("#workarea").attr("class", name);
cur_properties = (selectedElements[0] && selectedElements[0].nodeName == 'text') ? cur_text : cur_shape;
current_mode = name;
};