Start undo work

This commit is contained in:
acer 2020-12-04 17:52:02 -05:00
parent fcf53cf71a
commit 0a6d3fe874
44 changed files with 1243 additions and 368 deletions

View File

@ -0,0 +1,92 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Autoresize_canvas_action extends Base_action {
/**
* autoresize canvas to layer size, based on dimensions, up - always, if 1 layer - down.
*
* @param {int} width
* @param {int} height
* @param {int} layer_id
* @param {boolean} can_automate
*/
constructor(width, height, layer_id, can_automate = true) {
super('autoresize_canvas', 'Auto-resize Canvas');
this.width = width;
this.height = height;
this.layer_id = layer_id;
this.can_automate = can_automate;
this.old_config_width = null;
this.old_config_height = null;
}
async do() {
super.do();
const width = this.width;
const height = this.height;
const can_automate = this.can_automate;
let need_fit = false;
let new_config_width = config.WIDTH;
let new_config_height = config.HEIGHT;
// Resize up
if (width > new_config_width || height > new_config_height) {
const wrapper = document.getElementById('main_wrapper');
const page_w = wrapper.clientWidth;
const page_h = wrapper.clientHeight;
if (width > page_w || height > page_h) {
need_fit = true;
}
if (width > new_config_width)
new_config_width = parseInt(width);
if (height > new_config_height)
new_config_height = parseInt(height);
}
// Resize down
if (config.layers.length == 1 && can_automate !== false) {
if (width < new_config_width)
new_config_width = parseInt(width);
if (height < new_config_height)
new_config_height = parseInt(height);
}
if (new_config_width !== config.WIDTH || new_config_height !== height) {
this.old_config_width = config.WIDTH;
this.old_config_height = config.HEIGHT;
config.WIDTH = new_config_width;
config.HEIGHT = new_config_height;
app.GUI.prepare_canvas();
} else {
throw new Error('Aborted - Resize not necessary')
}
// Fit zoom when after short pause
// @todo - remove setTimeout
if (need_fit == true) {
await new Promise((resolve) => {
window.setTimeout(() => {
app.GUI.GUI_preview.zoom_auto();
resolve();
}, 100);
});
}
}
async undo() {
super.undo();
if (this.old_config_width != null) {
config.WIDTH = this.old_config_width;
}
if (this.old_config_height != null) {
config.HEIGHT = this.old_config_height;
}
if (this.old_config_width != null || this.old_config_height != null) {
app.GUI.prepare_canvas();
}
this.old_config_width = null;
this.old_config_height = null;
}
}

17
src/js/actions/base.js Normal file
View File

@ -0,0 +1,17 @@
export class Base_action {
constructor(action_id, action_description) {
this.action_id = action_id;
this.action_description = action_description;
this.is_done = false;
}
do() {
this.is_done = true;
}
undo() {
this.is_done = false;
}
free() {
// Override if need to run tasks to free memory when action is discarded from history
}
}

46
src/js/actions/bundle.js Normal file
View File

@ -0,0 +1,46 @@
import config from '../config.js';
import { Base_action } from './base.js';
export class Bundle_action extends Base_action {
/**
* Groups multiple actions together in the undo/redo history, runs them all at once.
*/
constructor(bundle_id, bundle_name, actions_to_do) {
super(bundle_id, bundle_name);
this.actions_to_do = actions_to_do;
}
async do() {
super.do();
let error = null;
let i = 0;
for (i = 0; i < this.actions_to_do.length; i++) {
try {
await this.actions_to_do[i].do();
} catch (e) {
error = e;
break;
}
}
// One of the actions aborted, undo all previous actions.
if (error) {
for (i--; i >= 0; i--) {
await this.actions_to_do[i].undo();
}
throw error;
}
config.need_render = true;
}
async undo() {
super.undo();
for (let i = this.actions_to_do.length - 1; i >= 0; i--) {
await this.actions_to_do[i].undo();
}
config.need_render = true;
}
free() {
this.actions_to_do = null;
}
}

View File

@ -0,0 +1,98 @@
import config from '../config.js';
import app from './../app.js';
import { Base_action } from './base.js';
export class Delete_layer_action extends Base_action {
/**
* removes layer
*
* @param {int} id
* @param {boolean} force - Force to delete first layer?
*/
constructor(layer_id, force) {
super('delete_layer', 'Delete Layer');
this.layer_id = parseInt(layer_id);
this.force = force || false;
this.insert_layer_action = null;
this.select_layer_action = null;
this.delete_index = null;
this.deleted_layer = null;
}
async do() {
super.do();
const id = this.layer_id;
const force = this.force;
// Determine if there is a layer to delete, abort if not
for (var i in config.layers) {
if (config.layers[i].id == id) {
this.delete_index = i;
}
}
if (this.delete_index === null) {
throw new Error('Aborted - Layer to delete not found');
}
if (config.layers.length == 1 && (force == undefined || force == false)) {
// Only 1 layer left
if (config.layer.type == null) {
//STOP
throw new Error('Aborted - Will not delete last layer');
}
else {
// Delete it, but before that - create new empty layer
this.insert_layer_action = new app.Actions.Insert_layer_action();
this.insert_layer_action.do();
}
}
if (config.layers.length > 1 && config.layer.id == id) {
// Select next or previous layer
try {
const select_action = new app.Actions.Select_next_layer_action(id);
await select_action.do();
this.select_layer_action = select_action;
} catch (error) {
const select_action = new app.Actions.Select_previous_layer_action(id);
await select_action.do();
this.select_layer_action = select_action;
}
}
// Remove layer from list
this.deleted_layer = config.layers.splice(this.delete_index, 1)[0];
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.deleted_layer) {
config.layers.splice(this.delete_index, 0, this.deleted_layer);
this.delete_index = null;
this.deleted_layer = null;
}
if (this.select_layer_action) {
await this.select_layer_action.undo();
this.select_layer_action = null;
}
if (this.insert_layer_action) {
await this.insert_layer_action.undo();
this.insert_layer_action = null;
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
if (this.deleted_layer) {
delete this.deleted_layer.link;
}
this.insert_layer_action = null;
this.select_layer_action = null;
this.deleted_layer = null;
}
}

14
src/js/actions/index.js Normal file
View File

@ -0,0 +1,14 @@
export { Autoresize_canvas_action } from './autoresize-canvas.js';
export { Bundle_action } from './bundle.js';
export { Delete_layer_action } from './delete-layer.js';
export { Init_canvas_zoom_action } from './init-canvas-zoom.js';
export { Insert_layer_action } from './insert-layer.js';
export { Prepare_canvas_action } from './prepare-canvas.js';
export { Reset_layers_action } from './reset-layers.js';
export { Reset_selection_action } from './reset-selection.js';
export { Select_layer_action } from './select-layer.js';
export { Select_next_layer_action } from './select-next-layer.js';
export { Select_previous_layer_action } from './select-previous-layer.js';
export { Toggle_layer_visibility_action } from './toggle-layer-visibility.js';
export { Update_config_action } from './update-config.js';
export { Update_layer_action } from './update-layer.js';

View File

@ -0,0 +1,45 @@
import app from '../app.js';
import config from '../config.js';
import zoomView from '../libs/zoomView.js';
import { Base_action } from './base.js';
export class Init_canvas_zoom_action extends Base_action {
/**
* Resets the canvas
*/
constructor() {
super('init_canvas_zoom', 'Initialize Canvas Zoom');
this.old_bounds = null;
this.old_context = null;
this.old_stable_dimensions = null;
}
async do() {
super.do();
this.old_bounds = zoomView.getBounds();
this.old_context = zoomView.getContext();
this.old_stable_dimensions = app.Layers.stable_dimensions;
zoomView.setBounds(0, 0, config.WIDTH, config.HEIGHT);
zoomView.setContext(app.Layers.ctx);
app.Layers.stable_dimensions = [
config.WIDTH,
config.HEIGHT
];
}
async undo() {
super.undo();
zoomView.setBounds(this.old_bounds.top, this.old_bounds.left, this.old_bounds.right, this.old_bounds.bottom);
zoomView.setContext(this.old_context);
app.Layers.stable_dimensions = this.old_stable_dimensions;
this.old_bounds = null;
this.old_context = null;
this.old_stable_dimensions = null;
}
free() {
this.old_bounds = null;
this.old_context = null;
this.old_stable_dimensions = null;
}
}

View File

@ -0,0 +1,205 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Insert_layer_action extends Base_action {
/**
* Creates new layer
*
* @param {object} settings
* @param {boolean} can_automate
*/
constructor(settings, can_automate = true) {
super('insert_layer', 'Insert Layer');
this.settings = settings;
this.can_automate = can_automate;
this.previous_auto_increment = null;
this.previous_selected_layer = null;
this.inserted_layer_id = null;
this.update_layer_action = null;
this.delete_layer_action = null;
this.autoresize_canvas_action = null;
}
async do() {
super.do();
this.previous_auto_increment = app.Layers.auto_increment;
this.previous_selected_layer = config.layer;
let autoresize_as = null;
// Default data
const layer = {
id: app.Layers.auto_increment,
parent_id: 0,
name: config.TOOL.name.charAt(0).toUpperCase() + config.TOOL.name.slice(1) + ' #' + app.Layers.auto_increment,
type: null,
link: null,
x: 0,
y: 0,
width: 0,
width_original: null,
height: 0,
height_original: null,
visible: true,
is_vector: false,
hide_selection_if_active: false,
opacity: 100,
order: app.Layers.auto_increment,
composition: 'source-over',
rotate: 0,
data: null,
params: {},
status: null,
color: config.COLOR,
filters: [],
render_function: null,
};
// Build data
for (let i in this.settings) {
if (typeof layer[i] == "undefined") {
alertify.error('Error: wrong key: ' + i);
continue;
}
layer[i] = this.settings[i];
}
// Prepare image
let image_load_promise;
if (layer.type == 'image') {
if(layer.name.toLowerCase().indexOf('.svg') == layer.name.length - 4){
// We have svg
layer.is_vector = true;
}
if (config.layers.length == 1 && config.layer.width == 0
&& config.layer.height == 0 && config.layer.data == null) {
// Remove first empty layer
this.delete_layer_action = new app.Actions.Delete_layer_action(config.layer.id, true);
this.delete_layer_action.do();
}
if (layer.link == null) {
if (typeof layer.data == 'object') {
// Load actual image
if (layer.width == 0)
layer.width = layer.data.width;
if (layer.height == 0)
layer.height = layer.data.height;
layer.link = layer.data.cloneNode(true);
layer.link.onload = function () {
config.need_render = true;
};
layer.data = null;
autoresize_as = [config.layer.width, config.layer.height];
need_autoresize = true;
}
else if (typeof layer.data == 'string') {
image_load_promise = new Promise((resolve, reject) => {
// Try loading as imageData
layer.link = new Image();
layer.link.onload = () => {
// Update dimensions
if (layer.width == 0)
layer.width = layer.link.width;
if (layer.height == 0)
layer.height = layer.link.height;
if (layer.width_original == null)
layer.width_original = layer.width;
if (layer.height_original == null)
layer.height_original = layer.height;
// Free data
layer.data = null;
autoresize_as = [layer.width, layer.height, layer.id, this.can_automate];
config.need_render = true;
resolve();
};
layer.link.onerror = (error) => {
resolve(error);
alertify.error('Sorry, image could not be loaded.');
};
layer.link.src = layer.data;
layer.link.crossOrigin = "Anonymous";
});
}
else {
alertify.error('Error: can not load image.');
}
}
}
if (this.settings != undefined && config.layers.length > 0
&& config.layer.width == 0 && config.layer.height == 0
&& config.layer.data == null && layer.type != 'image' && this.can_automate !== false) {
// Update existing layer, because it's empty
delete layer.name;
this.update_layer_action = new app.Actions.Update_layer_action(config.layer.id, layer);
await this.update_layer_action.do();
}
else {
// Create new layer
config.layers.push(layer);
config.layer = app.Layers.get_layer(layer.id);
app.Layers.auto_increment++;
if (config.layer == null) {
config.layer = config.layers[0];
}
this.inserted_layer_id = layer.id;
}
if (layer.id >= app.Layers.auto_increment)
app.Layers.auto_increment = layer.id + 1;
if (image_load_promise) {
await image_load_promise;
}
if (autoresize_as) {
this.autoresize_canvas_action = new app.Actions.Autoresize_canvas_action(...autoresize_as);
try {
await this.autoresize_canvas_action.do();
} catch(error) {
this.autoresize_canvas_action = null;
}
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
app.Layers.auto_increment = this.previous_auto_increment;
if (this.autoresize_canvas_action) {
await this.autoresize_canvas_action.undo();
this.autoresize_canvas_action = null;
}
if (this.inserted_layer_id) {
await new app.Actions.Delete_layer_action(this.inserted_layer_id, true).do();
this.inserted_layer_id = null;
}
if (this.update_layer_action) {
await this.update_layer_action.undo();
this.update_layer_action = null;
}
if (this.delete_layer_action) {
await this.delete_layer_action.undo();
this.delete_layer_action = null;
}
config.layer = this.previous_selected_layer;
this.previous_selected_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
this.delete_layer_action = null;
this.update_layer_action = null;
this.previous_selected_layer = null;
}
}

View File

@ -0,0 +1,29 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Prepare_canvas_action extends Base_action {
/**
* Resizes/renders the canvas at the specified step. Usually used on both sides of a config update action.
*
* @param {boolean} call_when
*/
constructor(call_when = 'undo') {
super('prepare_canvas', 'Prepare Canvas');
this.call_when = call_when;
}
async do() {
super.do();
if (this.call_when === 'do') {
app.GUI.prepare_canvas();
}
}
async undo() {
super.undo();
if (this.call_when === 'undo') {
app.GUI.prepare_canvas();
}
}
}

View File

@ -0,0 +1,53 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Reset_layers_action extends Base_action {
/*
* removes all layers
*/
constructor(auto_insert) {
super('reset_layers', 'Reset Layers');
this.auto_insert = auto_insert;
this.previous_auto_increment = null;
this.delete_actions = null;
this.insert_action = null;
}
async do() {
super.do();
const auto_insert = this.auto_insert;
this.previous_auto_increment = app.Layers.auto_increment;
this.delete_actions = [];
for (let i = config.layers.length - 1; i >= 0; i--) {
const delete_action = new app.Actions.Delete_layer_action(config.layers[i].id, true);
await delete_action.do();
this.delete_actions.push(delete_action);
}
app.Layers.auto_increment = 1;
if (auto_insert != undefined && auto_insert === true) {
const settings = {};
this.insert_action = new app.Actions.Insert_layer_action(settings);
await this.insert_action.do();
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.insert_action) {
await this.insert_action.undo();
this.insert_action = null;
}
for (let i = this.delete_actions.length - 1; i >= 0; i--) {
await this.delete_actions[i].undo();
}
app.Layers.auto_increment = this.previous_auto_increment;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}

View File

@ -0,0 +1,40 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Reset_selection_action extends Base_action {
/**
* Sets the selection to empty
*/
constructor() {
super('reset_selection', 'Reset Selection');
this.settings_reference = null;
this.old_settings_data = null;
}
async do() {
super.do();
this.settings_reference = app.Layers.Base_selection.find_settings();
this.old_settings_data = this.settings_reference.data;
this.settings_reference.data = {
x: null,
y: null,
width: null,
height: null
};
config.need_render = true;
}
async undo() {
super.undo();
this.settings_reference.data = this.old_settings_data;
this.settings_reference = null;
this.old_settings_data = null;
config.need_render = true;
}
free() {
this.settings_reference = null;
this.old_settings_data = null;
}
}

View File

@ -0,0 +1,57 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Select_layer_action extends Base_action {
/**
* marks layer as selected, active
*
* @param {int} layer_id
*/
constructor(layer_id, ignore_same_selection = false) {
super('select_layer', 'Select Layer');
this.reset_selection_action = null;
this.layer_id = parseInt(layer_id);
this.ignore_same_selection = ignore_same_selection;
this.old_layer = null;
}
async do() {
super.do();
let old_layer = config.layer;
let new_layer = app.Layers.get_layer(this.layer_id);
if (old_layer !== new_layer) {
this.old_layer = old_layer;
config.layer = new_layer;
} else if (!this.ignore_same_selection) {
throw new Error('Aborted - Layer already selected');
}
this.reset_selection_action = new app.Actions.Reset_selection_action();
await this.reset_selection_action.do();
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reset_selection_action) {
await this.reset_selection_action.undo();
this.reset_selection_action = null;
}
config.layer = this.old_layer;
this.old_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
this.old_layer = null;
}
}

View File

@ -0,0 +1,33 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Select_next_layer_action extends Base_action {
constructor(reference_layer_id) {
super('select_next_layer', 'Select Next Layer');
this.reference_layer_id = reference_layer_id;
this.old_config_layer = null;
}
async do() {
super.do();
const next_layer = app.Layers.find_next(this.reference_layer_id);
if (!next_layer) {
throw new Error('Aborted - Next layer to select not found');
}
this.old_config_layer = config.layer;
config.layer = next_layer;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
config.layer = this.old_config_layer;
this.old_config_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}

View File

@ -0,0 +1,33 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Select_previous_layer_action extends Base_action {
constructor(reference_layer_id) {
super('select_previous_layer', 'Select Previous Layer');
this.reference_layer_id = reference_layer_id;
this.old_config_layer = null;
}
async do() {
super.do();
const previous_layer = app.Layers.find_previous(this.reference_layer_id);
if (!previous_layer) {
throw new Error('Aborted - Previous layer to select not found');
}
this.old_config_layer = config.layer;
config.layer = previous_layer;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
config.layer = this.old_config_layer;
this.old_config_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}

View File

@ -0,0 +1,37 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Toggle_layer_visibility_action extends Base_action {
/**
* toggle layer visibility
*
* @param {int} layer_id
*/
constructor(layer_id) {
super('toggle_layer_visibility', 'Toggle Layer Visibility');
this.layer_id = parseInt(layer_id);
this.old_visible = null;
}
async do() {
super.do();
const layer = app.Layers.get_layer(this.layer_id);
this.old_visible = layer.visible;
if (layer.visible == false)
layer.visible = true;
else
layer.visible = false;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
const layer = app.Layers.get_layer(this.layer_id);
layer.visible = this.old_visible;
this.old_visible = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}

View File

@ -0,0 +1,37 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Update_config_action extends Base_action {
/**
* Updates the app config with the provided settings
*
* @param {object} settings
*/
constructor(settings) {
super('update_config', 'Update Config');
this.settings = settings;
this.old_settings = {};
}
async do() {
super.do();
for (let i in this.settings) {
this.old_settings[i] = config[i];
config[i] = this.settings[i];
}
}
async undo() {
super.undo();
for (let i in this.old_settings) {
config[i] = this.old_settings[i];
}
this.old_settings = {};
}
free() {
this.settings = null;
this.old_settings = null;
}
}

View File

@ -0,0 +1,54 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Update_layer_action extends Base_action {
/**
* Updates an existing layer with the provided settings
*
* @param {string} layer_id
* @param {object} settings
*/
constructor(layer_id, settings) {
super('update_layer', 'Update Layer');
this.layer_id = layer_id;
this.settings = settings;
this.reference_layer = null;
this.old_settings = {};
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
for (let i in this.settings) {
if (i == 'id')
continue;
if (i == 'order')
continue;
this.old_settings[i] = this.reference_layer[i];
this.reference_layer[i] = this.settings[i];
}
}
async undo() {
super.undo();
if (this.reference_layer) {
for (let i in this.old_settings) {
this.reference_layer[i] = this.old_settings[i];
}
this.old_settings = {};
}
this.reference_layer = null;
}
free() {
this.settings = null;
this.old_settings = null;
this.reference_layer = null;
}
}

11
src/js/app.js Normal file
View File

@ -0,0 +1,11 @@
// Store singletons for easy access
export default {
GUI: null,
Tools: null,
Layers: null,
Config: null,
State: null,
FileOpen: null,
FileSave: null,
Actions: null
};

View File

@ -3,6 +3,7 @@
* author: Vilius L.
*/
import app from './../app.js';
import config from './../config.js';
import Base_gui_class from './base-gui.js';
import Base_selection_class from './base-selection.js';
@ -66,7 +67,8 @@ class Base_layers_class {
*/
init() {
this.init_zoom_lib();
this.insert({});
new app.Actions.Insert_layer_action({}).do();
var sel_config = {
enable_background: false,
@ -315,151 +317,9 @@ class Base_layers_class {
* @param {boolean} can_automate
*/
async insert(settings, can_automate = true) {
var _this = this;
return new Promise(function(resolve, reject) {
var resolvable = false;
var need_autoresize = false;
//default data
var layer = {
id: _this.auto_increment,
parent_id: 0,
name: _this.Helper.ucfirst(config.TOOL.name) + ' #' + _this.auto_increment,
type: null,
link: null,
x: 0,
y: 0,
width: 0,
width_original: null,
height: 0,
height_original: null,
visible: true,
is_vector: false,
hide_selection_if_active: false,
opacity: 100,
order: _this.auto_increment,
composition: 'source-over',
rotate: 0,
data: null,
params: {},
status: null,
color: config.COLOR,
filters: [],
render_function: null,
};
//build data
for (var i in settings) {
if (typeof layer[i] == "undefined") {
alertify.error('Error: wrong key: ' + i);
continue;
}
layer[i] = settings[i];
}
//prepare image
if (layer.type == 'image') {
if(layer.name.toLowerCase().indexOf('.svg') == layer.name.length - 4){
//we have svg
layer.is_vector = true;
}
if (config.layers.length == 1 && config.layer.width == 0
&& config.layer.height == 0 && config.layer.data == null) {
//remove first empty layer?
_this.delete(config.layer.id, true);
}
if (layer.link == null) {
if (typeof layer.data == 'object') {
//load actual image
if (layer.width == 0)
layer.width = layer.data.width;
if (layer.height == 0)
layer.height = layer.data.height;
layer.link = layer.data.cloneNode(true);
layer.link.onload = function () {
config.need_render = true;
};
layer.data = null;
need_autoresize = true;
}
else if (typeof layer.data == 'string') {
//try loading as imageData
resolvable = true;
layer.link = new Image();
layer.link.onload = function () {
//update dimensions
if (layer.width == 0)
layer.width = layer.link.width;
if (layer.height == 0)
layer.height = layer.link.height;
if (layer.width_original == null)
layer.width_original = layer.width;
if (layer.height_original == null)
layer.height_original = layer.height;
//free data
layer.data = null;
_this.autoresize(layer.width, layer.height, layer.id, can_automate);
_this.render();
layer.link.onload = function () {
config.need_render = true;
};
resolve(true);
};
layer.link.onerror = function () {
alertify.error('Sorry, image could not be loaded.');
};
layer.link.src = layer.data;
layer.link.crossOrigin = "Anonymous";
}
else {
alertify.error('Error: can not load image.');
}
}
}
if (settings != undefined && config.layers.length > 0
&& config.layer.width == 0 && config.layer.height == 0
&& config.layer.data == null && layer.type != 'image' && can_automate !== false) {
//update existing layer, because its empty
for (var i in layer) {
if (i == 'id')
continue;
if (i == 'name')
continue;
if (i == 'order')
continue;
config.layer[i] = layer[i];
}
}
else {
//create new layer
config.layers.push(layer);
config.layer = _this.get_layer(layer.id);
_this.auto_increment++;
if (config.layer == null) {
config.layer = config.layers[0];
}
}
if (layer.id >= _this.auto_increment)
_this.auto_increment = layer.id + 1;
if (need_autoresize == true) {
_this.autoresize(config.layer.width, config.layer.height);
}
_this.render();
_this.Base_gui.GUI_layers.render_layers();
if(resolvable == false){
resolve(true);
}
});
return app.State.do_action(
new app.Actions.Insert_layer_action(settings, can_automate)
);
}
/**
@ -470,44 +330,10 @@ class Base_layers_class {
* @param {int} layer_id
* @param {boolean} can_automate
*/
autoresize(width, height, layer_id, can_automate = true) {
var _this = this;
var need_fit = false;
//resize up
if (width > config.WIDTH || height > config.HEIGHT) {
var wrapper = document.getElementById('main_wrapper');
var page_w = wrapper.clientWidth;
var page_h = wrapper.clientHeight;
if (width > page_w || height > page_h) {
need_fit = true;
}
if (width > config.WIDTH)
config.WIDTH = parseInt(width);
if (height > config.HEIGHT)
config.HEIGHT = parseInt(height);
}
//resize down
if (config.layers.length == 1 && can_automate !== false) {
if (width < config.WIDTH)
config.WIDTH = parseInt(width);
if (height < config.HEIGHT)
config.HEIGHT = parseInt(height);
}
this.Base_gui.prepare_canvas();
//fit zoom when after short pause
//@todo - remove setTimeout
if (need_fit == true) {
window.setTimeout(myCallback, 100);
function myCallback() {
_this.Base_gui.GUI_preview.zoom_auto();
}
}
async autoresize(width, height, layer_id, can_automate = true) {
return app.State.do_action(
new app.Actions.Autoresize_canvas_action(width, height, layer_id, can_automate)
);
}
/**
@ -535,60 +361,19 @@ class Base_layers_class {
* @param {int} id
* @param {boolean} force - Force to delete first layer?
*/
delete(id, force) {
id = parseInt(id);
if (config.layers.length == 1 && (force == undefined || force == false)) {
//only 1 layer left
if (config.layer.type == null) {
//STOP
return;
}
else {
//delete it, but before that - create new empty layer
this.insert();
}
}
if (config.layer.id == id) {
//select previous layer
config.layer = this.find_next(id);
if (config.layer == null)
config.layer = this.find_previous(id);
}
for (var i in config.layers) {
if (config.layers[i].id == id) {
//delete
if (config.layers[i].type == 'image') {
//clean image
config.layers[i].link = null;
}
config.layers.splice(i, 1);
}
}
this.render();
this.Base_gui.GUI_layers.render_layers();
async delete(id, force) {
return app.State.do_action(
new app.Actions.Delete_layer_action(id, force)
);
}
/*
* removes all layers
*/
reset_layers(auto_insert) {
for (var i = config.layers.length - 1; i >= 0; i--) {
this.delete(config.layers[i].id, true);
}
this.auto_increment = 1;
if (auto_insert != undefined && auto_insert === true) {
var settings = {};
this.insert(settings);
}
this.render();
this.Base_gui.GUI_layers.render_layers();
async reset_layers(auto_insert) {
return app.State.do_action(
new app.Actions.Reset_layers_action(auto_insert)
);
}
/**
@ -596,17 +381,10 @@ class Base_layers_class {
*
* @param {int} id
*/
toggle_visibility(id) {
id = parseInt(id);
var link = this.get_layer(id);
if (link.visible == false)
link.visible = true;
else
link.visible = false;
this.render();
this.Base_gui.GUI_layers.render_layers();
async toggle_visibility(id) {
return app.State.do_action(
new app.Actions.Toggle_layer_visibility_action(id)
);
}
/*
@ -621,13 +399,10 @@ class Base_layers_class {
*
* @param {int} id
*/
select(id) {
id = parseInt(id);
config.layer = this.get_layer(id);
this.Base_selection.reset_selection();
this.render();
this.Base_gui.GUI_layers.render_layers();
async select(id) {
return app.State.do_action(
new app.Actions.Select_layer_action(id)
);
}
/**

View File

@ -10,6 +10,9 @@ import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
let action_history = [];
let action_history_index = 0;
let action_history_max = 50;
/**
* Undo state class. Supports multiple levels undo.
@ -36,18 +39,71 @@ class Base_state_class {
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.code;
const key = event.key;
if (this.Helper.is_input(event.target))
return;
if (code == "KeyZ" && (event.ctrlKey == true || event.metaKey)) {
//undo
if (key == "z" && (event.ctrlKey == true || event.metaKey)) {
// Undo
this.undo();
event.preventDefault();
}
if (key == "y" && (event.ctrlKey == true || event.metaKey)) {
// Redo
this.redo();
event.preventDefault();
}
}, false);
}
async do_action(action) {
try {
await action.do();
} catch (error) {
// Action aborted. This could be expected behavior if detected that the action shouldn't run.
return { status: 'aborted', reason: error };
}
// Remove all redo actions from history
if (action_history_index < action_history.length) {
action_history = action_history.slice(0, action_history_index);
const freed_actions = action_history.slice(action_history_index, action_history.length);
for (let freed_action of freed_actions) {
freed_action.free();
}
}
// Add the new action to history
action_history.push(action);
if (action_history.length > action_history_max) {
action_history.shift();
} else {
action_history_index++;
}
return { status: 'completed' };
}
can_redo() {
return action_history_index < action_history.length;
}
can_undo() {
return action_history_index > 0;
}
async redo_action() {
if (this.can_redo()) {
const action = action_history[action_history_index];
await action.do();
action_history_index++;
}
}
async undo_action() {
if (this.can_undo()) {
action_history_index--;
await action_history[action_history_index].undo();
}
}
save() {
this.optimize();
@ -105,6 +161,8 @@ class Base_state_class {
* supports multiple levels undo system
*/
undo() {
this.undo_action();
/*
if (this.enabled == false || this.layers_archive[0] == undefined) {
//not saved yet
alertify.error('Undo is not available.');
@ -145,6 +203,11 @@ class Base_state_class {
this.Base_layers.select(data.layer_active);
this.layers_archive.shift(); //remove used state
*/
}
redo() {
this.redo_action();
}
/**

View File

@ -3,6 +3,7 @@
* author: Vilius L.
*/
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../base-layers.js';
import Helper_class from './../../libs/helpers.js';
@ -43,8 +44,9 @@ class GUI_layers_class {
var target = event.target;
if (target.id == 'insert_layer') {
//new layer
window.State.save();
_this.Base_layers.insert();
app.State.do_action(
new app.Actions.Insert_layer_action()
);
}
else if (target.id == 'layer_up') {
//move layer up
@ -58,18 +60,23 @@ class GUI_layers_class {
}
else if (target.id == 'visibility') {
//change visibility
_this.Base_layers.toggle_visibility(target.dataset.id);
return app.State.do_action(
new app.Actions.Toggle_layer_visibility_action(target.dataset.id)
);
}
else if (target.id == 'delete') {
//delete layer
window.State.save();
_this.Base_layers.delete(target.dataset.id);
app.State.do_action(
new app.Actions.Delete_layer_action(target.dataset.id)
);
}
else if (target.id == 'layer_name') {
//select layer
if (target.dataset.id == config.layer.id)
return;
_this.Base_layers.select(target.dataset.id);
app.State.do_action(
new app.Actions.Select_layer_action(target.dataset.id)
);
}
else if (target.id == 'delete_filter') {
//delete filter

View File

@ -7,7 +7,7 @@ const zoomView = (() => {
var im = invMatrix; // alias
var scale = 1; // current scale
const bounds = {
topLeft: 0,
top: 0,
left: 0,
right: 200,
bottom: 200,
@ -39,6 +39,9 @@ const zoomView = (() => {
getPosition() {
return { x: pos.x, y: pos.y };
},
getContext() {
return ctx;
},
getBounds() {
return bounds;
},

View File

@ -12,6 +12,7 @@ import './../css/menu.css';
import './../css/print.css';
import './../../node_modules/alertifyjs/build/css/alertify.min.css';
//js
import app from './app.js';
import config from './config.js';
import './core/components/index.js';
import Base_gui_class from './core/base-gui.js';
@ -20,9 +21,10 @@ import Base_tools_class from './core/base-tools.js';
import Base_state_class from './core/base-state.js';
import File_open_class from './modules/file/open.js';
import File_save_class from './modules/file/save.js';
import * as Actions from './actions/index.js';
window.addEventListener('load', function (e) {
//initiate app
// Initiate app
var Layers = new Base_layers_class();
var Base_tools = new Base_tools_class(true);
var GUI = new Base_gui_class();
@ -30,14 +32,24 @@ window.addEventListener('load', function (e) {
var File_open = new File_open_class();
var File_save = new File_save_class();
//register as global for quick or external access
// Register singletons in app module
app.Actions = Actions;
app.Config = config;
app.FileOpen = File_open;
app.FileSave = File_save;
app.GUI = GUI;
app.Layers = Layers;
app.State = Base_state;
app.Tools = Base_tools;
// Register as global for quick or external access
window.Layers = Layers;
window.AppConfig = config;
window.State = Base_state; // window.State.save();
window.FileOpen = File_open;
window.FileSave = File_save;
//render all
// Render all
GUI.load_modules();
GUI.load_default_values();
GUI.render_main_gui();

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_gui_class from './../../core/base-gui.js';
import Base_layers_class from './../../core/base-layers.js';
@ -87,39 +88,40 @@ class File_new_class {
width = dim[0];
height = dim[1];
}
if (transparency == true)
config.TRANSPARENCY = true;
else
config.TRANSPARENCY = false;
config.WIDTH = parseInt(width);
config.HEIGHT = parseInt(height);
config.ALPHA = 255;
config.COLOR = '#008000';
config.mouse = {};
config.visible_width = null;
config.visible_height = null;
this.Base_gui.prepare_canvas();
// Prepare layers
app.State.do_action(
new app.Actions.Bundle_action('new_file', 'New File', [
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
TRANSPARENCY: !!transparency,
WIDTH: parseInt(width),
HEIGHT: parseInt(height),
ALPHA: 255,
COLOR: '#008000',
mouse: {},
visible_width: null,
visible_height: null
}),
new app.Actions.Prepare_canvas_action('do'),
new app.Actions.Reset_layers_action(),
new app.Actions.Init_canvas_zoom_action(),
new app.Actions.Insert_layer_action({})
])
);
//prepare layers
this.Base_layers.reset_layers();
this.Base_layers.init_zoom_lib();
this.Base_layers.insert({});
config.need_render = true;
//last resolution
// Last resolution
var last_resolution = JSON.stringify([config.WIDTH, config.HEIGHT]);
this.Helper.setCookie('last_resolution', last_resolution);
//save_resolution
// Save resolution
if (save_resolution) {
this.Helper.setCookie('save_resolution', 1);
}
else {
this.Helper.setCookie('save_resolution', 0);
}
//transparency
// Save transparency
if (transparency) {
this.Helper.setCookie('transparency', 1);
}

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Base_gui_class from './../../core/base-gui.js';
@ -58,7 +59,9 @@ class File_open_class {
type: 'image',
data: data,
};
this.Base_layers.insert(new_layer);
app.State.do_action(
new app.Actions.Insert_layer_action(new_layer)
);
}
open_file() {
@ -125,8 +128,12 @@ class File_open_class {
width_original: width,
height_original: height,
};
this.Base_layers.insert(new_layer);
_this.Base_layers.autoresize(width, height);
app.State.do_action(
new app.Actions.Bundle_action('open_file_webcam', 'Open File Webcam', [
new app.Actions.Insert_layer_action(new_layer),
new app.Actions.Autoresize_canvas_action(width, height)
])
);
//destroy
if(track != null){
@ -207,8 +214,12 @@ class File_open_class {
width_original: img.width,
height_original: img.height,
};
_this.Base_layers.insert(new_layer);
_this.Base_layers.autoresize(img.width, img.height);
app.State.do_action(
new app.Actions.Bundle_action('open_file_data_url', 'Open File Data URL', [
new app.Actions.Insert_layer_action(new_layer),
new app.Actions.Autoresize_canvas_action(img.width, img.height)
])
);
img.onload = function () {
config.need_render = true;
};
@ -280,7 +291,9 @@ class File_open_class {
data: event.target.result,
order: order,
};
_this.Base_layers.insert(new_layer);
app.State.do_action(
new app.Actions.Insert_layer_action(new_layer)
);
_this.extract_exif(this.file);
}
else {
@ -365,8 +378,12 @@ class File_open_class {
img.onload = function () {
config.need_render = true;
};
_this.Base_layers.insert(new_layer);
_this.Base_layers.autoresize(img.width, img.height);
app.State.do_action(
new app.Actions.Bundle_action('open_file_url', 'Open File URL', [
new app.Actions.Insert_layer_action(new_layer),
new app.Actions.Autoresize_canvas_action(img.width, img.height)
])
);
};
img.onerror = function (ex) {
alertify.error('Sorry, image could not be loaded. Try copy image and paste it.');
@ -415,12 +432,19 @@ class File_open_class {
}
}
const actions = [];
//set attributes
config.ZOOM = 1;
config.WIDTH = parseInt(json.info.width);
config.HEIGHT = parseInt(json.info.height);
this.Base_layers.reset_layers();
this.Base_gui.prepare_canvas();
actions.push(
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
ZOOM: 1,
WIDTH: parseInt(json.info.width),
HEIGHT: parseInt(json.info.height)
}),
new app.Actions.Reset_layers_action(),
new app.Actions.Prepare_canvas_action('do'),
);
for (var i in json.layers) {
var value = json.layers[i];
@ -434,12 +458,18 @@ class File_open_class {
}
}
}
this.Base_layers.insert(value, false);
actions.push(
new app.Actions.Insert_layer_action(value, false)
);
}
if(json.info.layer_active != undefined) {
this.Base_layers.select(json.info.layer_active);
if (json.info.layer_active != undefined) {
actions.push(
new app.Actions.Select_layer_action(json.info.layer_active, true)
);
}
app.State.do_action(
new app.Actions.Bundle_action('open_json_file', 'Open JSON File', actions)
);
}
extract_exif(object) {

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
@ -112,10 +113,10 @@ class File_save_class {
if (config.layers[i].visible == false)
continue;
this.Base_layers.select(config.layers[i].id);
new app.Actions.Select_layer_action(config.layers[i].id, true).do();
_this.save_action(params, true);
}
this.Base_layers.select(active_layer);
new app.Actions.Select_layer_action(active_layer, true).do();
}
else {
_this.save_action(params);

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
@ -8,8 +9,9 @@ class Layer_delete_class {
}
delete() {
window.State.save();
this.Base_layers.delete(config.layer.id);
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
}
}

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
@ -79,12 +80,13 @@ class Layer_differences_class {
//show
if (canvas_preview == undefined) {
//main
window.State.save();
var params = [];
params.type = 'image';
params.name = 'Differences';
params.data = canvas.toDataURL("image/png");
this.Base_layers.insert(params);
app.State.do_action(
new app.Actions.Insert_layer_action(params)
);
}
else {
//preview

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
@ -56,7 +57,11 @@ class Layer_duplicate_class {
params.link = config.layer.link.cloneNode(true);
}
this.Base_layers.insert(params);
app.State.do_action(
new app.Actions.Bundle_action('duplicate_layer', 'Duplicate Layer', [
new app.Actions.Insert_layer_action(params)
])
);
}
}

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
@ -39,15 +40,20 @@ class Layer_flatten_class {
params.type = 'image';
params.name = 'Merged';
params.data = canvas.toDataURL("image/png");
this.Base_layers.insert(params);
//remove all layers
//remove rest of layers
let delete_actions = [];
for (var i = config.layers.length - 1; i >= 0; i--) {
if (config.layers[i].id == config.layer.id)
continue;
this.Base_layers.delete(config.layers[i].id);
delete_actions.push(new app.Actions.Delete_layer_action(config.layers[i].id));
}
console.log(delete_actions);
// Run actions
app.State.do_action(
new app.Actions.Bundle_action('flatten_image', 'Flatten Image', [
new app.Actions.Insert_layer_action(params),
...delete_actions
])
);
canvas.width = 1;
canvas.height = 1;

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Base_layers_class from './../../core/base-layers.js';
@ -42,11 +43,13 @@ class Layer_merge_class {
params.name = config.layer.name + ' + merged';
params.order = current_order;
params.data = canvas.toDataURL("image/png");
this.Base_layers.insert(params);
//remove old layer
this.Base_layers.delete(current_id);
this.Base_layers.delete(previous_id);
app.State.do_action(
new app.Actions.Bundle_action('merge_layers', 'Merge Layers', [
new app.Actions.Insert_layer_action(params),
new app.Actions.Delete_layer_action(current_id),
new app.Actions.Delete_layer_action(previous_id)
])
);
canvas.width = 1;
canvas.height = 1;

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import GUI_tools_class from './../../core/gui/gui-tools.js';
@ -31,9 +32,10 @@ class Layer_new_class {
}, false);
}
new () {
window.State.save();
this.Base_layers.insert();
new() {
app.State.do_action(
new app.Actions.Insert_layer_action()
);
}
new_selection() {
@ -83,7 +85,9 @@ class Layer_new_class {
type: 'image',
data: canvas.toDataURL("image/png"),
};
this.Base_layers.insert(params, false);
app.State.do_action(
new app.Actions.Insert_layer_action(params, false)
);
this.Selection.on_leave();
this.GUI_tools.activate_tool('select');

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
@ -13,8 +14,6 @@ class Layer_raster_class {
var current_layer = config.layer;
var current_id = current_layer.id;
window.State.save();
//show
var params = {
type: 'image',
@ -26,9 +25,12 @@ class Layer_raster_class {
height: canvas.height,
opacity: current_layer.opacity,
};
this.Base_layers.insert(params, false);
this.Base_layers.delete(current_id);
app.State.do_action(
new app.Actions.Bundle_action('convert_to_raster', 'Convert to Raster', [
new app.Actions.Insert_layer_action(params, false),
new app.Actions.Delete_layer_action(current_id)
])
);
}
}

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
@ -60,8 +61,6 @@ class Tools_borders_class {
}
add_borders(params) {
window.State.save();
//create borders layer
this.layer = {
name: 'Borders',
@ -75,7 +74,11 @@ class Tools_borders_class {
height: config.HEIGHT,
is_vector: true,
};
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('add_borders', 'Add Borders', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
}

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
@ -158,7 +159,11 @@ class Tools_keypoints_class {
params.y = parseInt(clone.dataset.y);
params.width = clone.width;
params.height = clone.height;
this.Base_layers.insert(params);
app.State.do_action(
new app.Actions.Bundle_action('keypoints', 'Key-Points', [
new app.Actions.Insert_layer_action(params)
])
);
clone.width = 1;
clone.height = 1;

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -18,6 +19,7 @@ class Animation_class extends Base_tools_class {
this.name = 'animation';
this.intervalID = null;
this.index = 0;
this.toggle_layer_visibility_action = new app.Actions.Toggle_layer_visibility_action();
this.disable_selection(ctx);
}
@ -102,7 +104,8 @@ class Animation_class extends Base_tools_class {
//show 1
if (config.layers[this.index] != undefined) {
_this.Base_layers.toggle_visibility(config.layers[this.index].id);
this.toggle_layer_visibility_action.layer_id = config.layers[this.index].id;
this.toggle_layer_visibility_action.do();
}
//change index

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -98,8 +99,6 @@ class Brush_class extends Base_tools_class {
if (mouse.valid == false || mouse.click_valid == false)
return;
window.State.save();
var params_hash = this.get_params_hash();
if (config.layer.type != this.name || params_hash != this.params_hash) {
@ -118,7 +117,11 @@ class Brush_class extends Base_tools_class {
rotate: null,
is_vector: true,
};
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('new_brush_layer', 'New Brush Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
this.params_hash = params_hash;
}

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -65,8 +66,6 @@ class Circle_class extends Base_tools_class {
if (mouse.valid == false || mouse.click_valid == false)
return;
window.State.save();
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
@ -85,7 +84,11 @@ class Circle_class extends Base_tools_class {
//disable rotate
this.layer.rotate = null;
}
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('new_circle_layer', 'New Circle Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
@ -131,7 +134,9 @@ class Circle_class extends Base_tools_class {
if (width == 0 && height == 0) {
//same coordinates - cancel
this.Base_layers.delete(config.layer.id);
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
return;
}

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -117,7 +118,11 @@ class Fill_class extends Base_tools_class {
params.y = parseInt(canvas.dataset.y) || 0;
params.width = canvas.width;
params.height = canvas.height;
this.Base_layers.insert(params);
app.State.do_action(
new app.Actions.Bundle_action('fill', 'Fill', [
new app.Actions.Insert_layer_action(params)
])
);
}
//prevent crash bug on touch screen - hard to explain and debug

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -74,8 +75,6 @@ class Gradient_class extends Base_tools_class {
is_vector = true;
}
window.State.save();
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
@ -93,7 +92,11 @@ class Gradient_class extends Base_tools_class {
center_y: mouse.y,
},
};
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('gradient', 'Gradient', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
@ -135,7 +138,9 @@ class Gradient_class extends Base_tools_class {
if (width == 0 && height == 0) {
//same coordinates - cancel
this.Base_layers.delete(config.layer.id);
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
return;
}

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -64,8 +65,6 @@ class Line_class extends Base_tools_class {
if (mouse.valid == false || mouse.click_valid == false)
return;
window.State.save();
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
@ -77,7 +76,11 @@ class Line_class extends Base_tools_class {
rotate: null,
is_vector: true,
};
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('line', 'Line', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
@ -116,7 +119,9 @@ class Line_class extends Base_tools_class {
if (width == 0 && height == 0) {
//same coordinates - cancel
this.Base_layers.delete(config.layer.id);
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
return;
}

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -95,7 +96,11 @@ class Pencil_class extends Base_tools_class {
rotate: null,
is_vector: true,
};
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('new_pencil_layer', 'New Pencil Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
this.params_hash = params_hash;
}
else {

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -64,8 +65,6 @@ class Rectangle_class extends Base_tools_class {
if (mouse.valid == false || mouse.click_valid == false)
return;
window.State.save();
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
@ -76,7 +75,11 @@ class Rectangle_class extends Base_tools_class {
y: mouse.y,
is_vector: true,
};
this.Base_layers.insert(this.layer);
app.State.do_action(
new app.Actions.Bundle_action('rectangle', 'Rectangle', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
mousemove(e) {
@ -142,7 +145,9 @@ class Rectangle_class extends Base_tools_class {
if (width == 0 && height == 0) {
//same coordinates - cancel
this.Base_layers.delete(config.layer.id);
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
return;
}

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -64,9 +65,9 @@ class Select_tool_class extends Base_tools_class {
//keyboard actions
document.addEventListener('keydown', (e) => {
if (config.TOOL.name != _this.name)
if (config.TOOL.name != this.name)
return;
if (_this.POP.active == true)
if (this.POP.active == true)
return;
if (this.Helper.is_input(e.target))
return;
@ -74,24 +75,26 @@ class Select_tool_class extends Base_tools_class {
//up
if (k == 38) {
_this.move(0, -1, e);
this.move(0, -1, e);
}
//down
else if (k == 40) {
_this.move(0, 1, e);
this.move(0, 1, e);
}
//right
else if (k == 39) {
_this.move(1, 0, e);
this.move(1, 0, e);
}
//left
else if (k == 37) {
_this.move(-1, 0, e);
this.move(-1, 0, e);
}
if (k == 46) {
//delete
if (config.TOOL.name == _this.name) {
_this.Base_layers.delete(config.layer.id);
if (config.TOOL.name == this.name) {
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
}
}
});
@ -166,7 +169,9 @@ class Select_tool_class extends Base_tools_class {
var canvas = this.Base_layers.convert_layer_to_canvas(value.id, null, false);
if (this.check_hit_region(e, canvas.getContext("2d")) == true) {
this.Base_layers.select(value.id);
app.State.do_action(
new app.Actions.Select_layer_action(value.id)
);
break;
}
}

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import zoomView from './../libs/zoomView.js';
import Base_tools_class from './../core/base-tools.js';
@ -2037,14 +2038,17 @@ class Text_class extends Base_tools_class {
this.selecting = true;
this.layer = existingLayer;
const editor = this.get_editor(this.layer);
this.Base_layers.select(existingLayer.id);
editor.trigger_cursor_start(this.layer, -1 + mouse.x - this.layer.x, mouse.y - this.layer.y);
app.State.do_action(
new app.Actions.Bundle_action('select_text_layer', 'Select Text Layer', [
new app.Actions.Select_layer_action(existingLayer.id)
])
);
this.Base_selection.set_selection(this.layer.x, this.layer.y, this.layer.width, this.layer.height);
}
else {
// Create a new text layer
this.creating = true;
window.State.save();
const layer = {
type: this.name,
params: {
@ -2062,7 +2066,11 @@ class Text_class extends Base_tools_class {
rotate: null,
is_vector: true,
};
this.Base_layers.insert(layer);
app.State.do_action(
new app.Actions.Bundle_action('new_text_layer', 'New Text Layer', [
new app.Actions.Insert_layer_action(layer)
])
);
this.layer = config.layer;
this.Base_selection.set_selection(mouse.x, mouse.y, 0, 0);
}