diff --git a/css/window.css b/css/window.css index 32c6e4bc..e1e9a8c9 100644 --- a/css/window.css +++ b/css/window.css @@ -3,8 +3,9 @@ #page_wrapper { display: grid; grid-template-columns: 332px auto 314px; - grid-template-rows: 30px minmax(200px, 5000px) 26px; + grid-template-rows: 30px 30px minmax(200px, 5000px) 26px; grid-template-areas: + "tab_bar tab_bar tab_bar" "left_bar toolbar toolbar" "left_bar center right_bar" "left_bar status_bar right_bar"; @@ -35,6 +36,9 @@ z-index: 250; } + #tab_bar { + grid-area: tab_bar; + } .sidebar { background-color: var(--color-ui);; display: flex; @@ -283,6 +287,7 @@ display: grid; grid-template-rows: auto minmax(200px, 5000px) 26px 36px; grid-template-areas: + "tab_bar" "toolbar" "center" "status_bar" @@ -403,6 +408,65 @@ border-bottom: 3px solid var(--color-accent); } +/* Tab Bar */ + #tab_bar { + display: flex; + flex-direction: row; + position: relative; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + padding-left: 4px; + } + #tab_bar .project_tab { + background-color: var(--color-back); + cursor: pointer; + width: 200px; + min-width: 120px; + height: 100%; + padding: 2px 6px; + position: relative; + overflow: hidden; + display: inline-flex; + margin-left: 2px; + border-top: 2px solid transparent; + } + #tab_bar .project_tab.selected { + background-color: var(--color-ui); + cursor: default; + border-top-color: var(--color-accent); + } + #tab_bar .project_tab:not(.selected):hover { + background-color: var(--color-button); + color: var(--color-light); + } + #tab_bar .project_tab > label { + overflow: hidden; + width: calc(100% - 20px); + flex: 1 1 auto; + padding: 0 4px; + } + #tab_bar .project_tab > * { + cursor: inherit + } + #tab_bar .project_tab_close_button { + flex: 0 0 24px; + text-align: center; + cursor: pointer; + display: none; + } + #tab_bar .project_tab:hover .project_tab_close_button, + #tab_bar .project_tab.selected .project_tab_close_button, + #tab_bar .project_tab .project_tab_close_button.unsaved { + display: block; + } + #tab_bar .project_tab_close_button:hover { + color: var(--color-light); + } + #tab_bar .project_tab_close_button.unsaved > i { + font-size: 13px; + text-align: center; + } /*Start Screen*/ diff --git a/index.html b/index.html index ce63e5bb..fb0e8805 100644 --- a/index.html +++ b/index.html @@ -647,6 +647,15 @@ -
- check - close -
diff --git a/js/animations/animation.js b/js/animations/animation.js index ec68669d..c9a05a8a 100644 --- a/js/animations/animation.js +++ b/js/animations/animation.js @@ -1606,11 +1606,11 @@ const Animator = { let content = Animator.buildFile(filter_path, true); if (isApp && !path) { - path = ModelMeta.export_path + path = Project.export_path var exp = new RegExp(osfs.replace('\\', '\\\\')+'models'+osfs.replace('\\', '\\\\')) var m_index = path.search(exp) if (m_index > 3) { - path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(ModelMeta.export_path, true) + path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(Project.export_path, true) } path = path.replace(/(\.geo)?\.json$/, '.animation.json') } @@ -1723,12 +1723,12 @@ BARS.defineActions(function() { category: 'animation', condition: {modes: ['animate'], method: () => Format.animation_files}, click: function () { - var path = ModelMeta.export_path + var path = Project.export_path if (isApp) { var exp = new RegExp(osfs.replace('\\', '\\\\')+'models'+osfs.replace('\\', '\\\\')) var m_index = path.search(exp) if (m_index > 3) { - path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(ModelMeta.export_path).replace(/\.geo/, '.animation') + path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(Project.export_path).replace(/\.geo/, '.animation') } } Blockbench.import({ diff --git a/js/blockbench.js b/js/blockbench.js index f5bcf6cb..fe10d5bd 100644 --- a/js/blockbench.js +++ b/js/blockbench.js @@ -20,7 +20,6 @@ var Prop = { file_name : '', added_models : 0, recording : null, - project_saved : true, fps : 0, progress : 0, session : false, diff --git a/js/desktop.js b/js/desktop.js index 998ff462..be15b63e 100644 --- a/js/desktop.js +++ b/js/desktop.js @@ -151,9 +151,9 @@ function addRecentProject(data) { } updateRecentProjects() } -function updateRecentProjectThumbnail() { +async function updateRecentProjectThumbnail() { if (elements.length == 0) return; - let path = ModelMeta.export_path || ModelMeta.save_path; + let path = Project.export_path || Project.save_path; let project = recent_projects.find(p => p.path == path); if (!project) return; @@ -167,16 +167,18 @@ function updateRecentProjectThumbnail() { let size = Math.max(box[0], box[1]*2) MediaPreview.camera.position.multiplyScalar(size/50) - MediaPreview.screenshot({crop: false}, url => { - let hash = project.path.hashCode().toString().replace(/^-/, '0'); - let path = PathModule.join(app.getPath('userData'), 'thumbnails', `${hash}.png`) - Blockbench.writeFile(path, { - savetype: 'image', - content: url + await new Promise((resolve, reject) => { + MediaPreview.screenshot({crop: false}, url => { + let hash = project.path.hashCode().toString().replace(/^-/, '0'); + let path = PathModule.join(app.getPath('userData'), 'thumbnails', `${hash}.png`) + Blockbench.writeFile(path, { + savetype: 'image', + content: url + }, resolve) + let store_path = project.path; + project.path = ''; + project.path = store_path; }) - let store_path = project.path; - project.path = ''; - project.path = store_path; }) // Clean old files @@ -377,7 +379,7 @@ function showSaveDialog(close) { unsaved_textures++; } }) - if ((window.Prop && Prop.project_saved === false && (elements.length > 0 || Group.all.length > 0)) || unsaved_textures) { + if ((window.Prop && Project.saved === false && (elements.length > 0 || Group.all.length > 0)) || unsaved_textures) { var answer = electron.dialog.showMessageBoxSync(currentwindow, { type: 'question', buttons: [tl('dialog.save'), tl('dialog.discard'), tl('dialog.cancel')], diff --git a/js/interface/actions.js b/js/interface/actions.js index d93a88c0..cf904838 100644 --- a/js/interface/actions.js +++ b/js/interface/actions.js @@ -1340,9 +1340,9 @@ const BARS = { new Action('open_model_folder', { icon: 'folder_open', category: 'file', - condition: () => {return isApp && (ModelMeta.save_path || ModelMeta.export_path)}, + condition: () => {return isApp && (Project.save_path || Project.export_path)}, click: function () { - shell.showItemInFolder(ModelMeta.export_path || ModelMeta.save_path); + shell.showItemInFolder(Project.export_path || Project.save_path); } }) new Action('open_backup_folder', { diff --git a/js/io/codec.js b/js/io/codec.js index 3b214e0b..0f66d2fd 100644 --- a/js/io/codec.js +++ b/js/io/codec.js @@ -28,8 +28,8 @@ class Codec { } if (file.path && isApp && this.remember && !file.no_file ) { var name = pathToName(file.path, true); - ModelMeta.name = pathToName(name, false); - ModelMeta.export_path = file.path; + Project.name = pathToName(name, false); + Project.export_path = file.path; addRecentProject({ name, path: file.path, @@ -58,10 +58,10 @@ class Codec { }, path => scope.afterDownload(path)) } fileName() { - return ModelMeta.name||Project.name||'model'; + return Project.name||'model'; } startPath() { - return ModelMeta.export_path; + return Project.export_path; } write(content, path) { var scope = this; @@ -74,7 +74,7 @@ class Codec { //overwrite(content, path, cb) afterDownload(path) { if (this.remember) { - Prop.project_saved = true; + Project.saved = true; } Blockbench.showQuickMessage(tl('message.save_file', [path ? pathToName(path, true) : this.fileName()])); } @@ -82,12 +82,12 @@ class Codec { var name = pathToName(path, true) if (Format.codec == this || this.id == 'project') { if (this.id == 'project') { - ModelMeta.save_path = path; + Project.save_path = path; } else { - ModelMeta.export_path = path; + Project.export_path = path; } - ModelMeta.name = pathToName(path, false); - Prop.project_saved = true; + Project.name = pathToName(path, false); + Project.saved = true; } if (this.remember) { addRecentProject({ diff --git a/js/io/format.js b/js/io/format.js index 3665aca6..dd40253e 100644 --- a/js/io/format.js +++ b/js/io/format.js @@ -1,19 +1,5 @@ var Format = 0; const Formats = {}; -const ModelMeta = { - save_path: '', - export_path: '', - animation_path: '', - _name: '', - get name() {return this._name}, - set name(name) { - this._name = name; - Project.name = this._name; - setProjectTitle(this._name) - }, - get saved() {return Prop.project_saved}, - set saved(s) {Prop.project_saved = !!s}, -} //Formats class ModelFormat { @@ -110,7 +96,7 @@ class ModelFormat { Undo.history.empty(); Undo.index = 0; - ModelMeta.export_path = ''; + Project.export_path = ''; var old_format = Format this.select(true) diff --git a/js/io/formats/bbmodel.js b/js/io/formats/bbmodel.js index 545e6241..dd4d34ad 100644 --- a/js/io/formats/bbmodel.js +++ b/js/io/formats/bbmodel.js @@ -14,8 +14,8 @@ var codec = new Codec('project', { newProject(model.meta.type||'free'); var name = pathToName(file.path, true); if (file.path && isApp && !file.no_file ) { - ModelMeta.save_path = file.path; - ModelMeta.name = pathToName(name, false); + Project.save_path = file.path; + Project.name = pathToName(name, false); addRecentProject({ name, path: file.path, @@ -246,8 +246,8 @@ BARS.defineActions(function() { keybind: new Keybind({key: 83, ctrl: true, alt: true}), click: function () { saveTextures(true) - if (isApp && ModelMeta.save_path) { - codec.write(codec.compile(), ModelMeta.save_path); + if (isApp && Project.save_path) { + codec.write(codec.compile(), Project.save_path); } else { codec.export() } diff --git a/js/io/formats/bedrock.js b/js/io/formats/bedrock.js index be1a0644..29a4f189 100644 --- a/js/io/formats/bedrock.js +++ b/js/io/formats/bedrock.js @@ -25,7 +25,7 @@ window.BedrockEntityManager = { } }, getEntityFile() { - var path = ModelMeta.export_path.split(osfs); + var path = Project.export_path.split(osfs); var name = path.pop().replace(/\.json$/, '').replace(/\.geo$/, ''); var root_index = path.indexOf('models'); path.splice(root_index); @@ -245,7 +245,7 @@ window.BedrockEntityManager = { path = mob } if (path) { - var texture_path = ModelMeta.export_path.split(osfs) + var texture_path = Project.export_path.split(osfs) var index = texture_path.lastIndexOf('models') - texture_path.length texture_path.splice(index) texture_path = [...texture_path, 'textures', 'entity', ...path.split('/')].join(osfs) @@ -873,7 +873,7 @@ var codec = new Codec('bedrock', { }) }, fileName() { - var name = ModelMeta.name||Project.name||'model'; + var name = Project.name||'model'; if (!name.match(/\.geo$/)) { name += '.geo'; } diff --git a/js/io/io.js b/js/io/io.js index ff65a44a..57817bd8 100644 --- a/js/io/io.js +++ b/js/io/io.js @@ -543,10 +543,10 @@ BARS.defineActions(function() { if (isApp) { saveTextures() if (Format) { - if (ModelMeta.export_path && Format.codec && Format.codec.compile) { - Format.codec.write(Format.codec.compile(), ModelMeta.export_path) - } else if (ModelMeta.save_path) { - Codecs.project.write(Codecs.project.compile(), ModelMeta.save_path); + if (Project.export_path && Format.codec && Format.codec.compile) { + Format.codec.write(Format.codec.compile(), Project.export_path) + } else if (Project.save_path) { + Codecs.project.write(Codecs.project.compile(), Project.save_path); } else if (Format.codec) { Format.codec.export() } @@ -582,11 +582,11 @@ BARS.defineActions(function() { type: 'Zip Archive', extensions: ['zip'], name: 'assets', - startpath: ModelMeta.export_path, + startpath: Project.export_path, content: content, savetype: 'zip' }) - Prop.project_saved = true; + Project.saved = true; }) } }) diff --git a/js/io/project.js b/js/io/project.js index b7baf4c1..2cb77ab7 100644 --- a/js/io/project.js +++ b/js/io/project.js @@ -1,12 +1,24 @@ class ModelProject { - constructor() { + constructor(options = {}) { for (var key in ModelProject.properties) { ModelProject.properties[key].reset(this); } + this.uuid = guid(); + this.selected = false; this._box_uv = false; this._texture_width = 16; this._texture_height = 16; + + this._name = ''; + this.saved = true; + this.save_path = ''; + this.export_path = ''; + + this.undo = new UndoSystem(); + this.format = options.format || Formats.free; + + ModelProject.all.push(this); } extend() { for (var key in ModelProject.properties) { @@ -27,15 +39,23 @@ class ModelProject { Vue.nextTick(updateProjectResolution) this._texture_width = n; } + get optional_box_uv() { + return Format.optional_box_uv; + } set texture_height(n) { n = parseInt(n)||16 Vue.nextTick(updateProjectResolution) this._texture_height = n; } - get optional_box_uv() { - return Format.optional_box_uv; + get name() { + return this._name; + } + set name(name) { + this._name = name; + setProjectTitle(this._name); } reset() { + return; if (isApp) updateRecentProjectThumbnail(); Blockbench.dispatchEvent('reset_project'); @@ -75,9 +95,8 @@ class ModelProject { this.overrides = null; Blockbench.display_settings = display = {}; - ModelMeta.save_path = ModelMeta.export_path = ModelMeta.name = ''; - ModelMeta.saved = true; - Prop.project_saved = true; + Project.save_path = Project.export_path = Project.name = ''; + Project.saved = true; Prop.added_models = 0; Canvas.updateAll(); Outliner.vue.$forceUpdate(); @@ -92,6 +111,57 @@ class ModelProject { delete Animator.motion_trail_lock; $('#var_placeholder_area').val(''); } + + /* + ----- THINGS TO SAVE -------- + textures + elements + groups + selection + display settings + format + + ----- SAVE EXTERNALLY -------- + scene + materials, + bones, + 3d elements + + + + */ + openSettings() { + BarItems.project_window.click(); + } + select() { + if (this.selected) return; + if (Project) { + Project.unselect() + } + Project = this; + Undo = this.undo; + this.selected = true; + + this.format.select(); + } + unselect() { + this.selected = false; + } + async close(force) { + + if (force || showSaveDialog()) { + if (isApp) await updateRecentProjectThumbnail(); + + Blockbench.dispatchEvent('close_project'); + + ModelProject.all.remove(this); + ModelProject.all[0].select(); + + return true; + } else { + return false; + } + } } new Property(ModelProject, 'string', 'name', { label: 'dialog.project.name' @@ -137,27 +207,22 @@ new Property(ModelProject, 'boolean', 'layered_textures', { }); -const Project = new ModelProject(); +ModelProject.all = []; + + +let Project = 0;// = new ModelProject(); //New function resetProject() { Project.reset() } -function newProject(format, force) { - if (force || showSaveDialog()) { - if (Format) { - Project.reset(); - } - if (format instanceof ModelFormat) { - format.select(); - } - Modes.options.edit.select(); - Blockbench.dispatchEvent('new_project'); - return true; - } else { - return false; - } +function newProject(format) { + new ModelProject({format}).select(); + + Modes.options.edit.select(); + Blockbench.dispatchEvent('new_project'); + return true; } // Resolution @@ -209,6 +274,15 @@ function updateProjectResolution() { document.querySelector('#project_resolution_status').textContent = `${Project.texture_width} ⨉ ${Project.texture_height}`; } +onVueSetup(() => { + Interface.tab_bar = new Vue({ + el: '#tab_bar', + data: { + projects: ModelProject.all + } + }) +}) + BARS.defineActions(function() { diff --git a/js/texturing/textures.js b/js/texturing/textures.js index 9ca91af9..d6066128 100644 --- a/js/texturing/textures.js +++ b/js/texturing/textures.js @@ -458,8 +458,8 @@ class Texture { generateFolder(path) { if (path.includes(osfs+'optifine'+osfs+'cit'+osfs)) { - if (ModelMeta.export_path) { - let model_arr = ModelMeta.export_path.split(osfs).slice(0, -1); + if (Project.export_path) { + let model_arr = Project.export_path.split(osfs).slice(0, -1); let tex_arr = path.split(osfs).slice(0, -1); let index = 0; tex_arr.find((dir, i) => { @@ -868,8 +868,8 @@ class Texture { if (Format.bone_rig && Project.geometry_name) { find_path = BedrockEntityManager.findEntityTexture(Project.geometry_name, true) } - if (!find_path && ModelMeta.export_path) { - var arr = ModelMeta.export_path.split(osfs); + if (!find_path && Project.export_path) { + var arr = Project.export_path.split(osfs); var index = arr.lastIndexOf('models'); if (index > 1) arr.splice(index, 256, 'textures') if (scope.folder) arr = arr.concat(scope.folder.split('/')); @@ -1283,8 +1283,8 @@ BARS.defineActions(function() { var arr = textures[0].path.split(osfs) arr.splice(-1) start_path = arr.join(osfs) - } else if (ModelMeta.export_path) { - var arr = ModelMeta.export_path.split(osfs) + } else if (Project.export_path) { + var arr = Project.export_path.split(osfs) arr.splice(-3) arr.push('textures') start_path = arr.join(osfs) diff --git a/js/undo.js b/js/undo.js index 21723e13..13efaefc 100644 --- a/js/undo.js +++ b/js/undo.js @@ -1,6 +1,8 @@ -var Undo = { - index: 0, - history: [], +class UndoSystem { + constructor() { + this.index = 0; + this.history = []; + } initEdit(aspects) { if (aspects && aspects.cubes) { console.warn('Aspect "cubes" is deprecated. Please use "elements" instead.'); @@ -8,113 +10,113 @@ var Undo = { } /* if ( - aspects && Undo.current_save && - Objector.equalKeys(aspects, Undo.current_save.aspects) && + aspects && this.current_save && + Objector.equalKeys(aspects, this.current_save.aspects) && aspects.elements !== selected && - Undo.history.length == Undo.index + this.history.length == this.index ) { return; } - This still causes issues, for example with different texture selections */ - Undo.current_save = new Undo.save(aspects) - return Undo.current_save; - }, + this.current_save = new UndoSystem.save(aspects) + return this.current_save; + } finishEdit(action, aspects) { if (aspects && aspects.cubes) { console.warn('Aspect "cubes" is deprecated. Please use "elements" instead.'); aspects.elements = aspects.cubes; } - if (!Undo.current_save) return; - aspects = aspects || Undo.current_save.aspects + if (!this.current_save) return; + aspects = aspects || this.current_save.aspects //After Blockbench.dispatchEvent('finish_edit', {aspects}) var entry = { - before: Undo.current_save, - post: new Undo.save(aspects), + before: this.current_save, + post: new UndoSystem.save(aspects), action: action } - Undo.current_save = entry.post - if (Undo.history.length > Undo.index) { - Undo.history.length = Undo.index; - delete Undo.current_save; + this.current_save = entry.post + if (this.history.length > this.index) { + this.history.length = this.index; + delete this.current_save; } - Undo.history.push(entry) + this.history.push(entry) - if (Undo.history.length > settings.undo_limit.value) { - Undo.history.shift() + if (this.history.length > settings.undo_limit.value) { + this.history.shift() } - Undo.index = Undo.history.length + this.index = this.history.length if (!aspects || !aspects.keep_saved) { - Prop.project_saved = false; + Project.saved = false; } Blockbench.dispatchEvent('finished_edit', {aspects}) if (EditSession.active) { EditSession.sendEdit(entry) } return entry; - }, + } cancelEdit() { - if (!Undo.current_save) return; + if (!this.current_save) return; outlines.children.length = 0 - Undo.loadSave(Undo.current_save, new Undo.save(Undo.current_save.aspects)) - delete Undo.current_save; - }, + this.loadSave(this.current_save, new UndoSystem.save(this.current_save.aspects)) + delete this.current_save; + } addKeyframeCasualties(arr) { if (!arr || arr.length == 0) return; - if (!Undo.current_save.keyframes) { - Undo.current_save.keyframes = { + if (!this.current_save.keyframes) { + this.current_save.keyframes = { animation: Animation.selected.uuid } } arr.forEach(kf => { - Undo.current_save.affected = true - Undo.current_save.keyframes[kf.uuid] = kf.getUndoCopy(); + this.current_save.affected = true + this.current_save.keyframes[kf.uuid] = kf.getUndoCopy(); }) - }, + } undo(remote) { - if (Undo.history.length <= 0 || Undo.index < 1) return; + if (this.history.length <= 0 || this.index < 1) return; - Prop.project_saved = false; - Undo.index--; + Project.saved = false; + this.index--; - var entry = Undo.history[Undo.index] - Undo.loadSave(entry.before, entry.post) + var entry = this.history[this.index] + this.loadSave(entry.before, entry.post) if (EditSession.active && remote !== true) { EditSession.sendAll('command', 'undo') } Blockbench.dispatchEvent('undo', {entry}) - }, + } redo(remote) { - if (Undo.history.length <= 0) return; - if (Undo.index >= Undo.history.length) { + if (this.history.length <= 0) return; + if (this.index >= this.history.length) { return; } - Prop.project_saved = false; + Project.saved = false; - var entry = Undo.history[Undo.index] - Undo.index++; - Undo.loadSave(entry.post, entry.before) + var entry = this.history[this.index] + this.index++; + this.loadSave(entry.post, entry.before) if (EditSession.active && remote !== true) { EditSession.sendAll('command', 'redo') } Blockbench.dispatchEvent('redo', {entry}) - }, + } remoteEdit(entry) { - Undo.loadSave(entry.post, entry.before, 'session') + this.loadSave(entry.post, entry.before, 'session') if (entry.save_history !== false) { - delete Undo.current_save; - Undo.history.push(entry) - if (Undo.history.length > settings.undo_limit.value) { - Undo.history.shift() + delete this.current_save; + this.history.push(entry) + if (this.history.length > settings.undo_limit.value) { + this.history.shift() } - Undo.index = Undo.history.length - Prop.project_saved = false; + this.index = this.history.length + Project.saved = false; Blockbench.dispatchEvent('finished_edit', {remote: true}) } - }, + } getItemByUUID(list, uuid) { if (!list || typeof list !== 'object' || !list.length) {return false;} var i = 0; @@ -125,93 +127,7 @@ var Undo = { i++; } return false; - }, - save: function(aspects) { - var scope = this; - this.aspects = aspects; - - if (aspects.selection) { - this.selection = [] - selected.forEach(function(obj) { - scope.selection.push(obj.uuid) - }) - if (Group.selected) { - this.selection_group = Group.selected.uuid - } - } - - if (aspects.elements) { - this.elements = {} - aspects.elements.forEach(function(obj) { - scope.elements[obj.uuid] = obj.getUndoCopy(aspects) - }) - } - - if (aspects.outliner) { - this.outliner = compileGroups(true) - } - - if (aspects.group) { - this.group = aspects.group.getChildlessCopy(true) - } - - if (aspects.textures) { - this.textures = {} - aspects.textures.forEach(t => { - var tex = t.getUndoCopy(aspects.bitmap) - this.textures[t.uuid] = tex - }) - } - - if (aspects.texture_order && Texture.all.length) { - this.texture_order = []; - Texture.all.forEach(tex => { - this.texture_order.push(tex.uuid); - }) - } - - if (aspects.selected_texture && Texture.all.length) { - this.selected_texture = Texture.selected ? Texture.selected.uuid : null; - } - - if (aspects.settings) { - this.settings = aspects.settings - } - - if (aspects.uv_mode) { - this.uv_mode = { - box_uv: Project.box_uv, - width: Project.texture_width, - height: Project.texture_height - } - } - - if (aspects.animations) { - this.animations = {} - aspects.animations.forEach(a => { - scope.animations[a.uuid] = a.getUndoCopy(); - }) - } - if (aspects.keyframes && Animation.selected && Timeline.animators.length) { - this.keyframes = { - animation: Animation.selected.uuid - } - aspects.keyframes.forEach(kf => { - scope.keyframes[kf.uuid] = kf.getUndoCopy() - }) - } - - if (aspects.display_slots) { - scope.display_slots = {} - aspects.display_slots.forEach(slot => { - if (display[slot]) { - scope.display_slots[slot] = display[slot].copy() - } else { - scope.display_slots[slot] = null - } - }) - } - }, + } loadSave(save, reference, mode) { var is_session = mode === 'session'; @@ -311,7 +227,7 @@ var Undo = { Painter.current = {} for (var uuid in save.textures) { if (reference.textures[uuid]) { - var tex = Undo.getItemByUUID(textures, uuid) + var tex = this.getItemByUUID(textures, uuid) if (tex) { var require_reload = tex.mode !== save.textures[uuid].mode; tex.extend(save.textures[uuid]).updateSource() @@ -327,7 +243,7 @@ var Undo = { } for (var uuid in reference.textures) { if (!save.textures[uuid]) { - var tex = Undo.getItemByUUID(Texture.all, uuid) + var tex = this.getItemByUUID(Texture.all, uuid) if (tex) { Texture.all.splice(Texture.all.indexOf(tex), 1) } @@ -363,7 +279,7 @@ var Undo = { if (save.animations) { for (var uuid in save.animations) { - var animation = (reference.animations && reference.animations[uuid]) ? Undo.getItemByUUID(Animator.animations, uuid) : null; + var animation = (reference.animations && reference.animations[uuid]) ? this.getItemByUUID(Animator.animations, uuid) : null; if (!animation) { animation = new Animation() animation.uuid = uuid @@ -375,7 +291,7 @@ var Undo = { } for (var uuid in reference.animations) { if (!save.animations[uuid]) { - var animation = Undo.getItemByUUID(Animator.animations, uuid) + var animation = this.getItemByUUID(Animator.animations, uuid) if (animation) { animation.remove(false) } @@ -460,12 +376,104 @@ var Undo = { } } } -Undo.save.prototype.addTexture = function(texture) { - if (!this.textures) return; - if (this.aspects.textures.safePush(texture)) { - this.textures[texture.uuid] = texture.getUndoCopy(this.aspects.bitmap) +UndoSystem.save = class { + constructor(aspects) { + + var scope = this; + this.aspects = aspects; + + if (aspects.selection) { + this.selection = [] + selected.forEach(function(obj) { + scope.selection.push(obj.uuid) + }) + if (Group.selected) { + this.selection_group = Group.selected.uuid + } + } + + if (aspects.elements) { + this.elements = {} + aspects.elements.forEach(function(obj) { + scope.elements[obj.uuid] = obj.getUndoCopy(aspects) + }) + } + + if (aspects.outliner) { + this.outliner = compileGroups(true) + } + + if (aspects.group) { + this.group = aspects.group.getChildlessCopy(true) + } + + if (aspects.textures) { + this.textures = {} + aspects.textures.forEach(t => { + var tex = t.getUndoCopy(aspects.bitmap) + this.textures[t.uuid] = tex + }) + } + + if (aspects.texture_order && Texture.all.length) { + this.texture_order = []; + Texture.all.forEach(tex => { + this.texture_order.push(tex.uuid); + }) + } + + if (aspects.selected_texture && Texture.all.length) { + this.selected_texture = Texture.selected ? Texture.selected.uuid : null; + } + + if (aspects.settings) { + this.settings = aspects.settings + } + + if (aspects.uv_mode) { + this.uv_mode = { + box_uv: Project.box_uv, + width: Project.texture_width, + height: Project.texture_height + } + } + + if (aspects.animations) { + this.animations = {} + aspects.animations.forEach(a => { + scope.animations[a.uuid] = a.getUndoCopy(); + }) + } + if (aspects.keyframes && Animation.selected && Timeline.animators.length) { + this.keyframes = { + animation: Animation.selected.uuid + } + aspects.keyframes.forEach(kf => { + scope.keyframes[kf.uuid] = kf.getUndoCopy() + }) + } + + if (aspects.display_slots) { + scope.display_slots = {} + aspects.display_slots.forEach(slot => { + if (display[slot]) { + scope.display_slots[slot] = display[slot].copy() + } else { + scope.display_slots[slot] = null + } + }) + } + } + addTexture(texture) { + if (!this.textures) return; + if (this.aspects.textures.safePush(texture)) { + this.textures[texture.uuid] = texture.getUndoCopy(this.aspects.bitmap) + } } } + +let Undo = null; + BARS.defineActions(function() { new Action('undo', { @@ -474,7 +482,9 @@ BARS.defineActions(function() { condition: () => (!open_dialog || open_dialog === 'uv_dialog' || open_dialog === 'toolbar_edit'), work_in_dialog: true, keybind: new Keybind({key: 90, ctrl: true}), - click: Undo.undo + click(e) { + Project.undo.undo(e); + } }) new Action('redo', { icon: 'redo', @@ -482,6 +492,8 @@ BARS.defineActions(function() { condition: () => (!open_dialog || open_dialog === 'uv_dialog' || open_dialog === 'toolbar_edit'), work_in_dialog: true, keybind: new Keybind({key: 89, ctrl: true}), - click: Undo.redo + click(e) { + Project.undo.redo(e); + } }) }) \ No newline at end of file diff --git a/js/web.js b/js/web.js index ce555851..6c52a67b 100644 --- a/js/web.js +++ b/js/web.js @@ -44,7 +44,7 @@ function loadInfoFromURL() { //Misc window.onbeforeunload = function() { - if (Prop.project_saved === false && elements.length > 0) { + if (Project.saved === false && elements.length > 0) { return 'Unsaved Changes'; } else { Blockbench.dispatchEvent('before_closing') @@ -58,7 +58,7 @@ function showSaveDialog(close) { unsaved_textures++; } }) - if ((Prop.project_saved === false && elements.length > 0) || unsaved_textures) { + if ((Project.saved === false && elements.length > 0) || unsaved_textures) { var answer = confirm(tl('message.close_warning.web')) return answer;