Skip to content

Commit 4786e41

Browse files
authored
Merge pull request #122 from Pascal-Institute/develop
Version Up
2 parents 9742c62 + 09abaa6 commit 4786e41

14 files changed

Lines changed: 434 additions & 319 deletions

css/paint_panel.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,15 @@
5555
flex-direction: column;
5656
margin-left: 5px;
5757
}
58+
59+
#watermarkPreviewImg {
60+
width: 36px;
61+
height: 36px;
62+
object-fit: contain;
63+
}
64+
65+
#padValue {
66+
width: 80px;
67+
margin-left: 5px;
68+
outline: none;
69+
}

css/panel.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ body {
3232
border-radius: 5px;
3333
display: flex;
3434
flex-direction: row;
35+
justify-content: space-between;
3536
align-items: center;
3637
width: 217px;
3738
border: 1px solid black;

imgkit/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ describe("ImageProcessor", () => {
10491049
```javascript
10501050
describe("ImageLayer", () => {
10511051
it("should maintain history", async () => {
1052-
const layer = new ImageLayer(renderer);
1052+
const layer = new ImageLayer();
10531053
await layer.openImage("test.png");
10541054
await layer.processImage({ blur: 2 });
10551055

imgkit/assets/spoid.png

-64 Bytes
Loading

imgkit/core/image_layer.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,20 @@ const { ipcRenderer } = require("electron");
1919
const { ImageLayerEvents } = require("../features/image_layer_events");
2020
const { ImageProcessor } = require("../processing/image_processor");
2121
const { ImageLoader } = require("../processing/image_loader");
22-
const { ImageMode, ModeManager } = require("../features/image_mode");
22+
const { ModeManager } = require("../features/image_mode");
2323
const { LayerHistory } = require("../features/layer_history");
2424
const { GifAnimation } = require("../features/gif_animation");
2525

26+
const LAYER_EVENT_CHANNEL = "imgkit-layer-event";
27+
let nextLayerId = 1;
28+
2629
/**
2730
* ImageLayer class - Represents a single image panel with canvas and controls
2831
*/
2932
class ImageLayer {
30-
constructor(renderer, isDefault = false) {
31-
this.renderer = renderer; // Reference to parent ImgKitRenderer
33+
constructor(isDefault = false) {
34+
this.id = `image-layer-${nextLayerId++}`;
3235
this.isDefault = isDefault; // Is this a placeholder "+ Add Image" layer?
33-
3436
// --------------------------------------------------------------------------
3537
// IMAGE DATA
3638
// --------------------------------------------------------------------------
@@ -79,14 +81,18 @@ class ImageLayer {
7981
* This clones the template and gets references to all UI elements
8082
*/
8183
createPanelFromTemplate() {
82-
if (!this.renderer.panelTemplate) {
84+
const template = document.getElementById("image-panel-template");
85+
if (!template) {
8386
console.error("Image panel template not found!");
8487
return;
8588
}
8689

8790
// Clone template content (defined in index.html)
88-
const clone = this.renderer.panelTemplate.content.cloneNode(true);
91+
const clone = template.content.cloneNode(true);
8992
this.panel = clone.querySelector(".imgPanel");
93+
if (this.panel) {
94+
this.panel.dataset.layerId = this.id;
95+
}
9096

9197
// Get canvas and drawing context
9298
this.canvas = this.panel.querySelector(".img-canvas");
@@ -214,9 +220,10 @@ class ImageLayer {
214220

215221
// Auto-scroll to show new image
216222
setTimeout(() => {
217-
this.renderer.updateScrollUI();
218-
this.renderer.scrollContainer.scrollLeft =
219-
this.renderer.scrollContainer.scrollWidth;
223+
ipcRenderer.send(LAYER_EVENT_CHANNEL, {
224+
type: "preview-updated",
225+
layerId: this.id,
226+
});
220227
}, 0);
221228
}
222229

imgkit/core/main_renderer.js

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
const { ipcRenderer } = require("electron");
1515
const { ImageLayer } = require("./image_layer.js");
1616
const { ImageMode } = require("../features/image_mode.js");
17+
const LAYER_EVENT_CHANNEL = "imgkit-layer-event";
1718

1819
// ============================================================================
1920
// MAIN CLASS: ImgKitRenderer
@@ -45,6 +46,7 @@ class ImgKitRenderer {
4546
this.setupScrollEvents();
4647
this.setupGlobalDragPrevention();
4748
this.setupGlobalMagnifyShortcut(); // Setup Alt + M globally
49+
this.setupLayerEventBridge();
4850
}
4951
}
5052

@@ -188,6 +190,139 @@ class ImgKitRenderer {
188190
});
189191
}
190192

193+
setupLayerEventBridge() {
194+
ipcRenderer.on(LAYER_EVENT_CHANNEL, async (event, payload) => {
195+
await this.handleLayerEvent(payload);
196+
});
197+
}
198+
199+
async handleLayerEvent(payload) {
200+
if (!payload || !payload.type) return;
201+
202+
switch (payload.type) {
203+
case "select":
204+
this.selectLayerById(payload.layerId, payload.ctrlKey);
205+
break;
206+
case "delete":
207+
this.selectLayerById(payload.layerId);
208+
this.deleteImage();
209+
break;
210+
case "copy":
211+
this.selectLayerById(payload.layerId);
212+
await this.copyImage();
213+
break;
214+
case "paste":
215+
this.selectLayerById(payload.layerId);
216+
await this.pasteImage();
217+
break;
218+
case "swap":
219+
this.swapLayersByIds(payload.fromLayerId, payload.toLayerId);
220+
break;
221+
case "drop":
222+
await this.handleDropFiles(payload.layerId, payload.files || []);
223+
break;
224+
case "preview-updated":
225+
this.updateScrollUI();
226+
this.scrollToEnd();
227+
break;
228+
case "navigate":
229+
this.navigateKeyboard(payload.direction, payload.ctrlKey);
230+
break;
231+
}
232+
}
233+
234+
selectLayerById(layerId, ctrlKey = false) {
235+
const index = this.getLayerIndexById(layerId);
236+
if (index !== -1) {
237+
this.setCurrentLayer(index, ctrlKey);
238+
}
239+
}
240+
241+
getLayerIndexById(layerId) {
242+
return this.imageLayerQueue.findIndex((layer) => layer.id === layerId);
243+
}
244+
245+
getLayerById(layerId) {
246+
const index = this.getLayerIndexById(layerId);
247+
return index === -1 ? null : this.imageLayerQueue[index];
248+
}
249+
250+
swapLayersByIds(fromLayerId, toLayerId) {
251+
const fromIndex = this.getLayerIndexById(fromLayerId);
252+
const toIndex = this.getLayerIndexById(toLayerId);
253+
if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return;
254+
this.swapLayers(fromIndex, toIndex);
255+
}
256+
257+
async handleDropFiles(targetLayerId, files) {
258+
if (!files || files.length === 0) return;
259+
260+
let dropHandled = false;
261+
262+
for (let i = 0; i < files.length; i++) {
263+
const entry = files[i];
264+
if (!entry) continue;
265+
266+
const targetLayer =
267+
i === 0 ? this.getLayerById(targetLayerId) : this.createImageLayer(false);
268+
if (!targetLayer) continue;
269+
270+
let success = false;
271+
if (entry.path) {
272+
success = await targetLayer.openImage(entry.path);
273+
} else if (entry.buffer) {
274+
const buffer = Buffer.isBuffer(entry.buffer)
275+
? entry.buffer
276+
: Buffer.from(entry.buffer);
277+
success = await targetLayer.openImageBuffer(buffer, entry.name);
278+
}
279+
280+
if (success) {
281+
targetLayer.panel.draggable = true;
282+
this.currentIndex = this.imageLayerQueue.indexOf(targetLayer);
283+
dropHandled = true;
284+
}
285+
}
286+
287+
if (dropHandled && this.currentIndex === this.imageLayerQueue.length - 1) {
288+
this.createDefaultImage();
289+
}
290+
}
291+
292+
scrollToEnd() {
293+
if (this.scrollContainer) {
294+
this.scrollContainer.scrollLeft = this.scrollContainer.scrollWidth;
295+
}
296+
}
297+
298+
navigateKeyboard(direction, ctrlKey) {
299+
if (!direction) return;
300+
301+
let newIndex = this.currentIndex;
302+
303+
if (direction === "left") {
304+
if (ctrlKey) {
305+
newIndex = 0;
306+
} else if (newIndex > 0) {
307+
newIndex -= 1;
308+
}
309+
} else if (direction === "right") {
310+
if (ctrlKey) {
311+
newIndex = this.imageLayerQueue.length - 1;
312+
} else if (newIndex < this.imageLayerQueue.length - 1) {
313+
newIndex += 1;
314+
}
315+
}
316+
317+
if (newIndex !== this.currentIndex) {
318+
this.setCurrentLayer(newIndex);
319+
const layer = this.imageLayerQueue[newIndex];
320+
if (layer && layer.panel) {
321+
layer.panel.focus();
322+
}
323+
}
324+
}
325+
191326
/**
192327
* Update scroll UI state and panel ordering
193328
* - Disables scroll buttons at edges
@@ -249,7 +384,7 @@ class ImgKitRenderer {
249384
* @returns {ImageLayer} Created image layer
250385
*/
251386
createImageLayer(isDefault = false) {
252-
const layer = new ImageLayer(this, isDefault);
387+
const layer = new ImageLayer(isDefault);
253388
this.imageLayerQueue.push(layer);
254389
this.scrollContainer.appendChild(layer.panel);
255390
this.updateScrollUI();
@@ -296,11 +431,7 @@ class ImgKitRenderer {
296431
* @returns {ImageLayer} Created default layer
297432
*/
298433
createDefaultImage() {
299-
if (this.imageLayerQueue.length > 0) {
300-
this.currentIndex++;
301-
}
302434
const layer = this.createImageLayer(true);
303-
this.currentIndex = this.imageLayerQueue.length - 1;
304435
layer.openImage("./assets/addImage.png");
305436
return layer;
306437
}

0 commit comments

Comments
 (0)