review, better view menu

This commit is contained in:
Hugh Bord 2021-12-24 14:06:55 +10:00
parent 0b3dcea9be
commit 3c34c799c3
8 changed files with 269 additions and 182 deletions

View File

@ -71,6 +71,7 @@ A most latest production build to use is available at https://asciibird.jewbird.
## Features to Add
* Save to PNG
* Delete blocks with Delete key needs undo/redo
* Layers undo and redo could be implemented, at the moment there isn't any.
* Warning on mirc export if ascii exceeds IRCs 512 per chat line limit.
* Review encodings check on file import - UTF8 vs Latin something
@ -79,6 +80,9 @@ A most latest production build to use is available at https://asciibird.jewbird.
## Bugs to fix
* Re add better width detection in ascii import
* Export to HTTP post shows success even if cancel
* If you brush off canvas you cannot undo the changed blocks
* If you apply an empty block from a brush, it will remove the char when it is supposed to leave the block alone.
* A bigger circle brush is a good example for this one.
* Can't type in dialogs

View File

@ -249,6 +249,8 @@ export default {
window.addEventListener("scroll", function (event) {
isThis.scrollOffset = this.scrollY;
});
this.mirror.x = this.toolbarState.mirrorX;
this.mirror.y = this.toolbarState.mirrorY;
},
destroyed() {
window.removeEventListener("scroll", function (event) {
@ -293,6 +295,10 @@ export default {
isShowingDialog: false,
drawBrush: false,
happy: false,
mirror: {
x: false,
y: false,
}
}),
computed: {
isDefault() {
@ -313,6 +319,64 @@ export default {
// options() {
// return this.$store.getters.options;
// },
asciibirdMeta() {
return this.$store.getters.asciibirdMeta;
},
debugPanelState() {
return this.$store.getters.debugPanel;
},
brushLibraryState() {
return this.$store.getters.brushLibraryState;
},
currentAscii() {
return this.$store.getters.currentAscii;
},
currentTab() {
return this.$store.getters.currentTab;
},
imageOverlay() {
return this.$store.getters.imageOverlay || false;
},
imageOverlayUrl() {
return this.imageOverlay.url
? this.imageOverlay.url.split("/").pop()
: "";
},
// Layers
asciiLayersMenu() {
let menu = [];
for (let i in [...this.currentAsciiLayers]) {
menu.push({
text: this.currentAsciiLayers[i].label,
click: () =>
this.$store.commit(
"changeLayer",
this.currentAsciiLayers.length - i
),
});
}
return menu.reverse();
},
currentAsciiLayers() {
return this.$store.getters.currentAsciiLayers;
},
selectedLayer() {
return this.$store.getters.selectedLayer;
},
canToggleLayer() {
return this.currentAsciiLayers.length > 1;
// We want to avoid hiding all the layers, so if there's only one
// visible left, we have to disable the buttons
},
// Toolbar related
gridView() {
return this.toolbarState.gridView;
},
canFg() {
return this.$store.getters.isTargettingFg;
},
@ -334,56 +398,8 @@ export default {
toolbarState() {
return this.$store.getters.toolbarState;
},
asciibirdMeta() {
return this.$store.getters.asciibirdMeta;
},
debugPanelState() {
return this.$store.getters.debugPanel;
},
brushLibraryState() {
return this.$store.getters.brushLibraryState;
},
currentAscii() {
return this.$store.getters.currentAscii;
},
currentTab() {
return this.$store.getters.currentTab;
},
currentAsciiLayers() {
return this.$store.getters.currentAsciiLayers;
},
selectedLayer() {
return this.$store.getters.selectedLayer;
},
canToggleLayer() {
return this.currentAsciiLayers.length > 1;
// We want to avoid hiding all the layers, so if there's only one
// visible left, we have to disable the buttons
},
imageOverlay() {
return this.$store.getters.imageOverlay || false;
},
imageOverlayUrl() {
return this.imageOverlay.url
? this.imageOverlay.url.split("/").pop()
: "";
},
asciiLayersMenu() {
let menu = [];
for (let i in [...this.currentAsciiLayers]) {
menu.push({
text: this.currentAsciiLayers[i].label,
click: () =>
this.$store.commit(
"changeLayer",
this.currentAsciiLayers.length - i
),
});
}
return menu.reverse();
},
// Toolbar menu
myMenu() {
let menu = [];
@ -445,23 +461,86 @@ export default {
},
{
text: "Toggle Grid",
icon: this.check2 ? "check_box" : "check_box_outline_blank",
click: (e) => {},
icon: this.gridView ? "check_box" : "check_box_outline_blank",
hotkey: "alt+g",
click: (e) => {this.$store.commit("toggleGridView", !this.gridView)},
},
{
text: "Mirror X",
icon: this.check2 ? "check_box" : "check_box_outline_blank",
click: (e) => {},
hotkey: "alt+x",
icon: this.toolbarState.mirrorX ? "check_box" : "check_box_outline_blank",
click: (e) => {
this.mirror.x = !this.toolbarState.mirrorX;
this.$store.commit("updateMirror", this.mirror);
this.$toasted.show(`Mirror Y ${this.mirror.y ? 'enabled' : 'disabled'}`);
},
},
{
text: "Mirror Y",
icon: this.check2 ? "check_box" : "check_box_outline_blank",
click: (e) => {},
icon: this.toolbarState.mirrorY ? "check_box" : "check_box_outline_blank",
hotkey: "alt+y",
click: (e) => {
this.mirror.y = !this.toolbarState.mirrorY;
this.$store.commit("updateMirror", this.mirror);
this.$toasted.show(`Mirror Y ${this.mirror.y ? 'enabled' : 'disabled'}`);
},
},
{
text: "Update Brush",
icon: this.check2 ? "check_box" : "check_box_outline_blank",
click: (e) => {},
hotkey: "alt+u",
icon: this.toolbarState.updateBrush ? "check_box" : "check_box_outline_blank",
click: (e) => {
this.$store.commit('toggleUpdateBrush', !this.toolbarState.updateBrush);
this.$toasted.show(`Update Brush when colours or char changes ${this.toolbarState.updateBrush ? 'enabled' : 'disabled'}`);
},
},
{
is: "separator",
},
{
text: "Swap FG and BG",
hotkey: "alt+r",
icon: "swap_horiz",
click: (e) => {
let bg = this.currentBg;
let fg = this.currentFg;
this.$store.commit("changeColourFg", bg);
this.$store.commit("changeColourBg", fg);
},
},
{
text: "Change FG",
hotkey: "alt+f",
icon: "flip_to_front",
click: (e) => {
this.$store.commit(
"changeIsUpdatingFg",
!this.toolbarState.isChoosingFg
);
},
},
{
text: "Change BG",
hotkey: "alt+b",
icon: "flip_to_back",
click: (e) => {
this.$store.commit(
"changeIsUpdatingBg",
!this.toolbarState.isChoosingBg
);
},
},
{
text: "Change Char",
hotkey: "alt+c",
icon: "atm",
click: (e) => {
this.$store.commit(
"changeIsUpdatingChar",
!this.toolbarState.isChoosingChar
);
},
},
{
is: "separator",
@ -531,6 +610,7 @@ export default {
{
text: "HTTP POST",
click: () => this.startExport("post"),
hotkey: "ctrl+shift+h",
icon: "post_add",
},
{
@ -553,7 +633,7 @@ export default {
click: () =>
this.$store.commit("toggleLayer", this.selectedLayer),
icon: "panorama_fish_eye",
hotkey: "ctrl+shift+v",
hotkey: "ctrl+shift+t",
disabled: !this.canToggleLayer,
},
{

View File

@ -63,7 +63,7 @@
</div>
</template>
<script>
import { toolbarIcons, mircColours99, blockWidth, blockHeight, exportMirc } from '../ascii';
import { toolbarIcons, mircColours99, blockWidth, blockHeight, exportMirc, mergeLayers } from '../ascii';
import LZString from "lz-string";
export default {
@ -104,9 +104,6 @@ export default {
currentAscii() {
return this.$store.getters.currentAscii;
},
currentAsciiBlocks() {
return this.$store.getters.currentAsciiBlocks;
},
asciiStats() {
// const compressed = ( JSON.stringify(this.currentAscii) / 1024).toFixed(2);
// const uncompressed = (JSON.stringify(this.currentAscii).length / 1024).toFixed(2);
@ -177,7 +174,7 @@ export default {
watch: {},
methods: {
copyUriToClipboard() {
let ascii = LZString.compressToEncodedURIComponent(JSON.stringify(this.currentAsciiBlocks));
let ascii = LZString.compressToEncodedURIComponent(JSON.stringify(mergeLayers()));
this.$copyText(ascii).then(
(e) => {

View File

@ -154,8 +154,8 @@ export default {
this.toolbar.w = this.toolbarState.w;
this.toolbar.h = this.toolbarState.h;
this.mirror.x = this.toolbarState.mirrorX;
this.mirror.y = this.toolbarState.mirrorY;
this.mirror.x = this.mirrorX;
this.mirror.y = this.mirrorY;
},
name: "Toolbar",
components: { Colours },
@ -221,11 +221,23 @@ export default {
updateBrush() {
return this.toolbarState.updateBrush;
},
mirrorX() {
return this.toolbarState.mirrorX;
},
mirrorY() {
return this.toolbarState.mirrorY;
},
},
watch: {
yOffset(val) {
this.$refs.toolbardrag.top = Number.parseInt(this.toolbarState.y + val);
},
mirrorX(val) {
this.mirror.x = val;
},
mirrorY(val) {
this.mirror.y = val;
},
},
methods: {
updateMirror() {

View File

@ -202,9 +202,6 @@ export default {
brushSizeType() {
return this.$store.getters.brushSizeType;
},
currentAsciiBlocks() {
return this.$store.getters.currentAsciiBlocks;
},
brushBlocks() {
return this.$store.getters.brushBlocks;
},

View File

@ -255,48 +255,6 @@ export default {
}
});
hotkeys("alt+g", function (event, handler) {
event.preventDefault();
_this.$store.commit("toggleGridView", !_this.gridView);
return;
});
hotkeys("alt+r", function (event, handler) {
event.preventDefault();
let bg = _this.currentBg;
let fg = _this.currentFg;
_this.$store.commit("changeColourFg", bg);
_this.$store.commit("changeColourBg", fg);
return;
});
hotkeys("alt+f", function (event, handler) {
event.preventDefault();
_this.$store.commit(
"changeIsUpdatingFg",
!_this.toolbarState.isChoosingFg
);
return;
});
hotkeys("alt+b", function (event, handler) {
event.preventDefault();
_this.$store.commit(
"changeIsUpdatingBg",
!_this.toolbarState.isChoosingBg
);
return;
});
hotkeys("alt+c", function (event, handler) {
event.preventDefault();
_this.$store.commit(
"changeIsUpdatingChar",
!_this.toolbarState.isChoosingChar
);
return;
});
hotkeys("Delete", "editor", function (event, handler) {
if (
@ -318,10 +276,11 @@ export default {
}
// Reset and hide the select after successful copy
_this.$store.commit(
"updateAsciiBlocks",
_this.currentAsciiLayerBlocks
);
_this.$store.dispatch("updateAsciiBlocksAsync", {
blocks: _this.currentAsciiLayerBlocks,
diff: { ... _this.diffBlocks },
});
_this.$emit("updatecanvas");
_this.$toasted.show("Deleted blocks!", {

View File

@ -443,7 +443,7 @@ export default new Vuex.Store({
if (prev.old) {
for(let change in prev.old) {
let data = prev.old[change];
tempLayers[prev.l].data[data.y][data.x] = data.b;
tempLayers[prev.l].data[data.y][data.x] = { ... data.b };
}
}
@ -664,11 +664,6 @@ export default new Vuex.Store({
currentChar: (state) => state.toolbarState.selectedChar,
currentTab: (state) => state.tab,
currentAscii: (state) => state.asciibirdMeta[state.tab] ?? false,
currentAsciiBlocks: (state) => {
let blocks = JSON.parse(LZString.decompressFromUTF16(state.asciibirdMeta[state.tab].current)) || [];
return blocks;
},
currentAsciiLayers: (state) => {
let layers = JSON.parse(LZString.decompressFromUTF16(state.asciibirdMeta[
state.tab].layers));

View File

@ -4,7 +4,6 @@
id="canvas-area"
@mouseleave="isMouseOnCanvas = false"
@mouseenter="isMouseOnCanvas = true"
>
<context-menu ref="editor-menu" class="z-50">
<ul>
@ -112,25 +111,25 @@ export default {
case "ArrowUp":
_this.y--;
_this.drawBrush(_this.isErasing);
_this.delayRedrawCanvas();
// _this.delayRedrawCanvas();
break;
case "ArrowDown":
_this.y++;
_this.drawBrush(_this.isErasing);
_this.delayRedrawCanvas();
// _this.delayRedrawCanvas();
break;
case "ArrowLeft":
_this.x--;
_this.drawBrush(_this.isErasing);
_this.delayRedrawCanvas();
// _this.delayRedrawCanvas();
break;
case "ArrowRight":
_this.x++;
_this.drawBrush(_this.isErasing);
_this.delayRedrawCanvas();
// _this.delayRedrawCanvas();
break;
case " ":
@ -139,6 +138,11 @@ export default {
_this.canTool = false;
_this.dispatchBlocks();
this.diffBlocks = {
l: this.selectedLayerIndex,
new: [],
old: [],
};
break;
}
}
@ -240,6 +244,12 @@ export default {
isTextEditing() {
return this.currentTool.name === "text";
},
isEraserFill() {
return this.currentTool.name === "fill-eraser";
},
isFill() {
return this.currentTool.name === "fill";
},
isTextEditingValues() {
return (
this.textEditing.startX !== null && this.textEditing.startY !== null
@ -359,9 +369,6 @@ export default {
canvasTransparent() {
return this.imageOverlay.visible ? "opacity: 0.6;" : "opacity: 1;";
},
currentAsciiBlocks() {
return this.$store.getters.currentAsciiBlocks;
},
},
watch: {
currentAscii(val, old) {
@ -376,6 +383,9 @@ export default {
this.warnInvisibleLayer();
}
},
currentAsciiLayerBlocks() {
this.delayRedrawCanvas();
},
currentTool() {
this.warnInvisibleLayer();
@ -428,16 +438,16 @@ export default {
textEditing(val, old) {
this.$emit("textediting", val);
},
// updateCanvas(val, old) {
// if (val !== old) {
// // This comes from KeyboardShortcuts.vue
// this.clearToolCanvas();
// this.drawTextIndicator();
// this.drawIndicator();
updateCanvas(val, old) {
if (val !== old) {
// This comes from KeyboardShortcuts.vue
this.clearToolCanvas();
this.drawTextIndicator();
this.drawIndicator();
// this.delayRedrawCanvas();
// }
// },
this.delayRedrawCanvas();
}
},
selecting(val) {
this.$emit("selecting", val);
},
@ -458,7 +468,7 @@ export default {
},
canvasKeyDown(char) {
// if (this.isTextEditing) {
console.log(char);
// console.log(char);
if (
this.currentAsciiLayerBlocks[this.textEditing.startY] &&
this.currentAsciiLayerBlocks[this.textEditing.startY][
@ -808,7 +818,6 @@ export default {
if (this.currentAsciiLayers.length) {
// https://stackoverflow.com/questions/28390358/high-cpu-usage-with-canvas-and-requestanimationframe
// Position of the meta array
let x = 0;
let y = 0;
@ -819,12 +828,17 @@ export default {
let curBlock = {};
// hack font for ascii shout outs 2 beenz
if (this.diffBlocks.new.length && !this.canTool) {
console.log("redrawing canvas from cache");
if (
this.diffBlocks.new.length &&
!this.canTool &&
!this.isTextEditing &&
!this.isErasing &&
!this.isEraserFill &&
!this.isFill
// Ignore redrawing from diffBlocks the above until they are refactored to work better
) {
// console.log("redrawing canvas from cache");
// If we have a difference stored, just render the difference only instead
// of the entire ascii again
@ -862,9 +876,8 @@ export default {
new: [],
old: [],
};
} else {
console.log("redrawing canvas");
// console.log("redrawing canvas");
this.ctx.save();
this.canvasRef.width = this.canvasRef.width;
@ -948,7 +961,7 @@ export default {
onCanvasDrag(x, y) {
this.top = y;
},
dispatchBlocks() {
dispatchBlocks(clearDiff = false) {
this.diffBlocks.old = this.diffBlocks.old.flat();
this.diffBlocks.new = this.diffBlocks.new.flat();
@ -957,11 +970,14 @@ export default {
diff: { ...this.diffBlocks },
});
// this.diffBlocks = {
// l: this.selectedLayerIndex,
// new: [],
// old: [],
// };
if (clearDiff) {
this.diffBlocks = {
l: this.selectedLayerIndex,
new: [],
old: [],
};
}
},
// Mouse Up, Down and Move
canvasMouseUp() {
@ -1015,14 +1031,12 @@ export default {
case "fill":
this.fill();
this.canTool = false;
this.dispatchBlocks();
this.dispatchBlocks(true);
break;
case "fill-eraser":
this.fill(true);
this.dispatchBlocks();
this.dispatchBlocks(true);
break;
case "brush":
@ -1105,7 +1119,7 @@ export default {
this.delayRedrawCanvas();
this.eraser();
}
break;
case "select":
@ -1149,8 +1163,8 @@ export default {
clearToolCanvas() {
if (this.toolCtx) {
this.toolCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.toolCtx.save();
this.$refs.canvastools.width = this.$refs.canvastools.width;
// this.toolCtx.save();
this.toolCtx.width = this.toolCtx.width;
}
},
delayRedrawCanvas() {
@ -1161,7 +1175,6 @@ export default {
requestAnimationFrame(() => {
_this.redrawCanvas();
_this.redraw = true;
});
}, 1000 / this.options.fps);
}
@ -1641,7 +1654,7 @@ export default {
}
}
},
async storeDiffBlocks(x, y, oldBlock, newBlock) {
storeDiffBlocks(x, y, oldBlock, newBlock) {
// For undo
if (!this.diffBlocks.old[y]) {
this.diffBlocks.old[y] = [];
@ -1784,11 +1797,20 @@ export default {
if (this.canBg) {
newColor.bg = this.currentBg;
current.bg = this.asciiBlockAtXy.bg;
if (newColor.bg === undefined) {
delete current['bg'];
}
}
//
if (this.canFg) {
newColor.fg = this.currentFg;
current.fg = this.asciiBlockAtXy.fg;
if (newColor.fg === undefined) {
delete current['fg'];
}
}
// If the newColor is same as the existing
@ -1804,8 +1826,12 @@ export default {
current,
eraser
);
if (eraser) {
this.delayRedrawCanvas();
}
},
fillTool(fillBlocks, y, x, current, eraser) {
fillTool(currentLayerBlocks, y, x, current, eraser) {
if (y >= Math.floor(this.canvas.height / blockHeight)) {
return;
}
@ -1814,45 +1840,62 @@ export default {
return;
}
if (fillBlocks[y] === undefined || fillBlocks[y][x] === undefined) {
if (currentLayerBlocks[y] === undefined || currentLayerBlocks[y][x] === undefined) {
return;
}
if (this.canFg && fillBlocks[y][x].fg !== current.fg) {
return;
}
let targetBlock = currentLayerBlocks[y][x];
if (this.canBg && fillBlocks[y][x].bg !== current.bg) {
// if (this.canFg && currentLayerBlocks[y][x].fg !== current.fg) {
// return;
// }
if (this.canBg && targetBlock.bg !== current.bg) {
return;
}
// We can eraser or fill
if (this.canBg) {
fillBlocks[y][x].bg = eraser ? undefined : this.currentBg;
if (!eraser) {
if (this.canBg) {
targetBlock.bg = this.currentBg;
}
if (this.canFg) {
targetBlock.fg = this.currentFg;
}
if (this.canText) {
targetBlock.char = this.currentChar;
}
} else {
if (this.canBg) {
delete targetBlock["bg"];
}
if (this.canFg) {
delete targetBlock["fg"];
}
if (this.canText) {
delete targetBlock["char"];
}
}
if (this.canFg) {
fillBlocks[y][x].fg = eraser ? undefined : this.currentFg;
}
if (this.canText) {
fillBlocks[y][x].char = eraser ? undefined : this.currentChar;
}
this.storeDiffBlocks(x, y, current, fillBlocks[y][x]);
// console.log({e:eraser, o: current, n: targetBlock });
this.storeDiffBlocks(x, y, current, targetBlock);
// Fill in all four directions
// Fill Prev row
this.fillTool(fillBlocks, y, x - 1, current, eraser);
this.fillTool(currentLayerBlocks, y, x - 1, current, eraser);
// Fill Next row
this.fillTool(fillBlocks, y, x + 1, current, eraser);
this.fillTool(currentLayerBlocks, y, x + 1, current, eraser);
// Fill Prev col
this.fillTool(fillBlocks, y - 1, x, current, eraser);
this.fillTool(currentLayerBlocks, y - 1, x, current, eraser);
// Fill next col
this.fillTool(fillBlocks, y + 1, x, current, eraser);
this.fillTool(currentLayerBlocks, y + 1, x, current, eraser);
},
},
};