change pane type

This commit is contained in:
Skyler Lehmkuhl 2024-12-02 14:40:40 -05:00
parent e0ba8ed8c3
commit 7778107e4d
6 changed files with 393 additions and 28 deletions

108
src/assets/infopanel.svg Normal file
View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
id="svg30571"
version="1.1"
inkscape:version="0.92pre1 unknown"
sodipodi:docname="gimp-information.svg">
<defs
id="defs30573" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="15.524752"
inkscape:cx="4.9851892"
inkscape:cy="17.36011"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:snap-page="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
showborder="false"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false"
showguides="false" />
<metadata
id="metadata30576">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>
image/svg+xml
</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>Klaus Staedtler </dc:title>
</cc:Agent>
</dc:creator>
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<g
id="g3940">
<g
inkscape:label="gimp-question"
id="gimp-question"
style="display:inline"
transform="translate(-141.0002,517.3622)">
<path
style="fill:#bebebe;fill-opacity:1;stroke:none"
d="m -92,302 c -4.418278,0 -8,3.58172 -8,8 0,4.41828 3.581722,8 8,8 4.418278,0 8,-3.58172 8,-8 0,-4.41828 -3.581722,-8 -8,-8 z m 0,2 c 3.313708,0 6,2.68629 6,6 0,3.31371 -2.686292,6 -6,6 -3.313708,0 -6,-2.68629 -6,-6 0,-3.31371 2.686292,-6 6,-6 z"
transform="translate(241.0002,217)"
id="path27976"
inkscape:connector-curvature="0" />
</g>
<text
transform="scale(1.3005016,0.76893407)"
sodipodi:linespacing="125%"
id="text3956"
y="1364.6973"
x="3.7694485"
style="font-style:normal;font-weight:normal;font-size:17.11714172px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.98779672px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="1364.6973"
x="3.7694485"
id="tspan3958"
sodipodi:role="line"
style="stroke-width:0.98779672px">i</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

143
src/assets/timeline.svg Normal file
View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 15.999999 16.00003"
id="svg7384"
height="16.000031"
width="16"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="gimp-video.svg">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview1509"
showgrid="false"
inkscape:zoom="14.749972"
inkscape:cx="-387.86346"
inkscape:cy="427.40701"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="svg7384" />
<metadata
id="metadata90">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:contributor>
<cc:Agent>
<dc:title>Barbara Muraus, Jakub Steiner, Klaus Staedtler</dc:title>
</cc:Agent>
</dc:contributor>
<dc:description>Images originally created as the &quot;Art Libre&quot; icon set. Extended and adopted for GIMP</dc:description>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs7386">
<linearGradient
osb:paint="solid"
id="linearGradient8074">
<stop
id="stop8072"
offset="0"
style="stop-color:#be00be;stop-opacity:1;" />
</linearGradient>
<linearGradient
osb:paint="solid"
id="linearGradient7561">
<stop
id="stop7558"
offset="0"
style="stop-color:#a5a5a5;stop-opacity:1;" />
</linearGradient>
<linearGradient
osb:paint="solid"
id="linearGradient7548">
<stop
id="stop7546"
offset="0"
style="stop-color:#ebebeb;stop-opacity:1;" />
</linearGradient>
<linearGradient
osb:paint="solid"
id="linearGradient7542">
<stop
id="stop7538"
offset="0"
style="stop-color:#c9c9c9;stop-opacity:1;" />
</linearGradient>
<linearGradient
gradientTransform="matrix(0,-735328.32,170712.69,0,2464326300,577972450)"
osb:paint="solid"
id="linearGradient19282">
<stop
id="stop19284"
offset="0"
style="stop-color:#b4b4b4;stop-opacity:1;" />
</linearGradient>
<linearGradient
gradientTransform="matrix(0.34682586,0,0,0.30620888,26.67918,122.03851)"
osb:paint="solid"
id="linearGradient19282-4">
<stop
id="stop19284-0"
offset="0"
style="stop-color:#bebebe;stop-opacity:1;" />
</linearGradient>
<linearGradient
gradientTransform="translate(576.89471,203.31841)"
gradientUnits="userSpaceOnUse"
y2="-14"
x2="-144"
y1="-14"
x1="-157"
id="linearGradient7027"
xlink:href="#linearGradient19282-4" />
</defs>
<g
transform="translate(-119.08356,-36.287169)"
style="display:inline"
id="stock">
<g
id="gimp-video"
style="display:inline"
transform="translate(-61.947694,-602.7128)">
<g
style="display:inline"
id="emblem-videos"
transform="translate(-380.96875,257)">
<g
id="g1873">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient7027);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50793636;marker:none;enable-background:new"
d="m -156,-22 c 0,0 0,0 0,2 l 0,12 c 0,2 -1,2 -1,2 l 1,0 c 0,0 1,0 1,-1 l 1,0 c 0,1 -1,1 -1,1 l 1,0 c 0,0 1,0 1,-1 l 6,0 c 0,1 -0.67515,1 -1,1 l 1,0 c 0,0 1,0 1,-1 l 1,0 c 0,1 -1,1 -1,1 l 1,0 c 0,0 1,0 1,-2 l 0,-12 c 0,-2 0,-2 0,-2 l -3,0 c 0,0 0,0 0,2 l 0,2 -6,0 0,-2 c 0,-2 0,-2 0,-2 z m 1,1 1,0 0,1 -1,0 z m 9,0 1,0 0,1 -1,0 z m -9,2 1,0 0,1 -1,0 z m 9,0 1,0 0,1 -1,0 z m -9,2 1,0 0,1 -1,0 z m 2,0 6,0 0,4 -6,0 z m 7,0 1,0 0,1 -1,0 z m -9,2 1,0 0,1 -1,0 z m 9,0 1,0 0,1 -1,0 z m -9,2 1,0 0,1 -1,0 z m 9,0 1,0 0,1 -1,0 z m -7,1 6,0 0,4 -6,0 z m -2,1 1,0 0,1 -1,0 z m 9,0 1,0 0,1 -1,0 z m -9,2 1,0 0,1 -1,0 z m 9,0 1,0 0,1 -1,0 z"
transform="translate(719.96895,404)"
id="rect5523"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -3,6 +3,7 @@ import * as fitCurve from '/fit-curve.js';
import { Bezier } from "/bezier.js"; import { Bezier } from "/bezier.js";
import { Quadtree } from './quadtree.js'; import { Quadtree } from './quadtree.js';
import { createNewFileDialog, showNewFileDialog, closeDialog } from './newfile.js'; import { createNewFileDialog, showNewFileDialog, closeDialog } from './newfile.js';
import { titleCase } from './utils.js';
const { writeTextFile: writeTextFile, readTextFile: readTextFile }= window.__TAURI__.fs; const { writeTextFile: writeTextFile, readTextFile: readTextFile }= window.__TAURI__.fs;
const { const {
open: openFileDialog, open: openFileDialog,
@ -748,10 +749,9 @@ class Shape {
let newCurves = [] let newCurves = []
let intersectMap = {} let intersectMap = {}
for (let i=0; i<this.curves.length-1; i++) { for (let i=0; i<this.curves.length-1; i++) {
console.log(this.quadtree.query(this.curves[i].bbox()))
// for (let j=i+1; j<this.curves.length; j++) { // for (let j=i+1; j<this.curves.length; j++) {
for (let j of this.quadtree.query(this.curves[i].bbox())) { for (let j of this.quadtree.query(this.curves[i].bbox())) {
if (i == j) continue; if (i >= j) continue;
let intersects = this.curves[i].intersects(this.curves[j]) let intersects = this.curves[i].intersects(this.curves[j])
if (intersects.length) { if (intersects.length) {
intersectMap[i] ||= [] intersectMap[i] ||= []
@ -872,7 +872,6 @@ class Shape {
} }
this.vertices.forEach((vertex, i) => { this.vertices.forEach((vertex, i) => {
console.log(i)
for (let i=0; i<Math.min(10,this.regions.length); i++) { for (let i=0; i<Math.min(10,this.regions.length); i++) {
let region = this.regions[i] let region = this.regions[i]
let regionVertexCurves = [] let regionVertexCurves = []
@ -1168,26 +1167,35 @@ async function greet() {
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
rootPane = document.querySelector("#root") rootPane = document.querySelector("#root")
rootPane.appendChild(createPane(toolbar())) rootPane.appendChild(createPane(panes.toolbar))
rootPane.addEventListener("mousemove", (e) => { rootPane.addEventListener("mousemove", (e) => {
mouseEvent = e; mouseEvent = e;
}) })
let [_toolbar, panel] = splitPane(rootPane, 10, true, createPane(timeline())) let [_toolbar, panel] = splitPane(rootPane, 10, true, createPane(panes.timeline))
let [stageAndTimeline, _infopanel] = splitPane(panel, 70, false, createPane(infopanel())) let [stageAndTimeline, _infopanel] = splitPane(panel, 70, false, createPane(panes.infopanel))
let [_timeline, _stage] = splitPane(stageAndTimeline, 30, false, createPane(stage())) let [_timeline, _stage] = splitPane(stageAndTimeline, 30, false, createPane(panes.stage))
}); });
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
updateAll() updateAll()
}) })
window.addEventListener("click", function(event) {
const popupMenu = document.getElementById("popupMenu");
// If the menu exists and the click is outside the menu and any button with the class 'paneButton', remove the menu
if (popupMenu && !popupMenu.contains(event.target) && !event.target.classList.contains("paneButton")) {
popupMenu.remove(); // Remove the menu from the DOM
}
})
window.addEventListener("keydown", (e) => { window.addEventListener("keydown", (e) => {
// let shortcuts = {} // let shortcuts = {}
// for (let shortcut of config.shortcuts) { // for (let shortcut of config.shortcuts) {
// shortcut = shortcut.split("+") // shortcut = shortcut.split("+")
// TODO // TODO
// } // }
console.log(e) // console.log(e)
if (e.key == config.shortcuts.playAnimation) { if (e.key == config.shortcuts.playAnimation) {
console.log("Spacebar pressed") console.log("Spacebar pressed")
} else if (e.key == config.shortcuts.undo && e.ctrlKey == true) { } else if (e.key == config.shortcuts.undo && e.ctrlKey == true) {
@ -1289,7 +1297,6 @@ async function saveAs() {
} }
async function open() { async function open() {
console.log("gonna open")
const path = await openFileDialog({ const path = await openFileDialog({
multiple: false, multiple: false,
directory: false, directory: false,
@ -1353,18 +1360,15 @@ async function quit() {
} }
function addFrame() { function addFrame() {
console.log(context.activeObject.currentFrameNum)
if (context.activeObject.currentFrameNum >= context.activeObject.activeLayer.frames.length) { if (context.activeObject.currentFrameNum >= context.activeObject.activeLayer.frames.length) {
for (let i=context.activeObject.activeLayer.frames.length; i<=context.activeObject.currentFrameNum; i++) { for (let i=context.activeObject.activeLayer.frames.length; i<=context.activeObject.currentFrameNum; i++) {
context.activeObject.activeLayer.frames.push(new Frame()) context.activeObject.activeLayer.frames.push(new Frame())
} }
console.log(context.activeObject.activeLayer)
updateLayers() updateLayers()
} }
} }
function addKeyframe() { function addKeyframe() {
console.log(context.activeObject.currentFrameNum)
let newKeyframe = new Frame("keyframe") let newKeyframe = new Frame("keyframe")
let latestFrame = context.activeObject.getFrame(Math.max(context.activeObject.currentFrameNum-1, 0)) let latestFrame = context.activeObject.getFrame(Math.max(context.activeObject.currentFrameNum-1, 0))
for (let key in latestFrame.keys) { for (let key in latestFrame.keys) {
@ -1381,7 +1385,6 @@ function addKeyframe() {
} else if (context.activeObject.activeLayer.frames[context.activeObject.currentFrameNum].frameType != "keyframe") { } else if (context.activeObject.activeLayer.frames[context.activeObject.currentFrameNum].frameType != "keyframe") {
context.activeObject.activeLayer.frames[context.activeObject.currentFrameNum] = newKeyframe context.activeObject.activeLayer.frames[context.activeObject.currentFrameNum] = newKeyframe
} }
console.log(context.activeObject.activeLayer)
updateLayers() updateLayers()
} }
@ -1509,8 +1512,6 @@ function stage() {
for (let curve of region.curves) { for (let curve of region.curves) {
intersect_count += curve.intersects(line).length intersect_count += curve.intersects(line).length
} }
console.log(region)
console.log(intersect_count)
if (intersect_count%2==1) { if (intersect_count%2==1) {
// region.fillStyle = context.fillStyle // region.fillStyle = context.fillStyle
actions.colorRegion.create(region, context.fillStyle) actions.colorRegion.create(region, context.fillStyle)
@ -1833,20 +1834,84 @@ function infopanel() {
createNewFileDialog(_newFile); createNewFileDialog(_newFile);
showNewFileDialog() showNewFileDialog()
function createPane(content=undefined) { function createPaneMenu(div) {
let div = document.createElement("div") const menuItems = ["Item 1", "Item 2", "Item 3"]; // The items for the menu
let header = document.createElement("div")
if (!content) { // Get the menu container (create a new div for the menu)
content = stage() // TODO: change based on type const popupMenu = document.createElement("div");
popupMenu.id = "popupMenu"; // Set the ID to ensure we can target it later
// Create a <ul> element to hold the list items
const ul = document.createElement("ul");
// Loop through the menuItems array and create a <li> for each item
for (let pane in panes) {
const li = document.createElement("li");
// Create the <img> element for the icon
const img = document.createElement("img");
img.src = `assets/${panes[pane].name}.svg`; // Use the appropriate SVG as the source
// img.style.width = "20px"; // Set the icon size
// img.style.height = "20px"; // Set the icon size
// img.style.marginRight = "10px"; // Add space between the icon and text
// Append the image to the <li> element
li.appendChild(img);
// Set the text of the item
li.appendChild(document.createTextNode(titleCase(panes[pane].name)));
li.addEventListener("click", () => {
createPane(panes[pane], div)
updateUI()
updateLayers()
updateAll()
popupMenu.remove()
})
ul.appendChild(li); // Append the <li> to the <ul>
} }
popupMenu.appendChild(ul); // Append the <ul> to the popupMenu div
document.body.appendChild(popupMenu); // Append the menu to the body
return popupMenu; // Return the created menu element
}
function createPane(paneType=undefined, div=undefined) {
if (!div) {
div = document.createElement("div")
} else {
div.textContent = ''
}
let header = document.createElement("div")
if (!paneType) {
paneType = panes.stage // TODO: change based on type
}
let content = paneType.func()
header.className = "header" header.className = "header"
let button = document.createElement("button") let button = document.createElement("button")
header.appendChild(button) header.appendChild(button)
let icon = document.createElement("img") let icon = document.createElement("img")
icon.className="icon" icon.className="icon"
icon.src = "/assets/stage.svg" icon.src = `/assets/${paneType.name}.svg`
button.appendChild(icon) button.appendChild(icon)
button.addEventListener("click", () => {
let popupMenu = document.getElementById("popupMenu");
// If the menu is already in the DOM, remove it
if (popupMenu) {
popupMenu.remove(); // Remove the menu from the DOM
} else {
// Create and append the new menu to the DOM
popupMenu = createPaneMenu(div);
// Position the menu below the button
const buttonRect = event.target.getBoundingClientRect();
popupMenu.style.left = `${buttonRect.left}px`;
popupMenu.style.top = `${buttonRect.bottom + window.scrollY}px`;
}
// Prevent the click event from propagating to the window click listener
event.stopPropagation();
})
div.className = "vertical-grid" div.className = "vertical-grid"
header.style.height = "calc( 2 * var(--lineheight))" header.style.height = "calc( 2 * var(--lineheight))"
@ -1941,8 +2006,8 @@ function updateLayers() {
let framescontainer = container.querySelectorAll(".frames-container")[0] let framescontainer = container.querySelectorAll(".frames-container")[0]
layerspanel.textContent = "" layerspanel.textContent = ""
framescontainer.textContent = "" framescontainer.textContent = ""
console.log(context.activeObject)
for (let layer of context.activeObject.layers) { for (let layer of context.activeObject.layers) {
// for (let i=0; i<5; i++) {
let layerHeader = document.createElement("div") let layerHeader = document.createElement("div")
layerHeader.className = "layer-header" layerHeader.className = "layer-header"
layerspanel.appendChild(layerHeader) layerspanel.appendChild(layerHeader)
@ -1959,14 +2024,9 @@ function updateLayers() {
}) })
let highlightedFrame = false let highlightedFrame = false
layer.frames.forEach((frame, i) => { layer.frames.forEach((frame, i) => {
// for (let j=0; j<5-i; j++) {
let frameEl = document.createElement("div") let frameEl = document.createElement("div")
frameEl.className = "frame" frameEl.className = "frame"
frameEl.setAttribute("frameNum", i) frameEl.setAttribute("frameNum", i)
// frameEl.addEventListener("click", () => {
// context.activeObject.currentFrameNum = frameEl.getAttribute("frameNum")
// updateLayers()
// })
if (i == context.activeObject.currentFrameNum) { if (i == context.activeObject.currentFrameNum) {
frameEl.classList.add("active") frameEl.classList.add("active")
highlightedFrame = true highlightedFrame = true
@ -2136,3 +2196,22 @@ async function updateMenu() {
await (macOS ? menu.setAsAppMenu() : menu.setAsWindowMenu()) await (macOS ? menu.setAsAppMenu() : menu.setAsWindowMenu())
} }
updateMenu() updateMenu()
const panes = {
stage: {
name: "stage",
func: stage
},
toolbar: {
name: "toolbar",
func: toolbar
},
timeline: {
name: "timeline",
func: timeline
},
infopanel: {
name: "infopanel",
func: infopanel
},
}

View File

@ -367,3 +367,33 @@ button {
#newFileDialog .dialog-button:hover { #newFileDialog .dialog-button:hover {
background-color: #0056b3; background-color: #0056b3;
} }
#popupMenu {
background-color: #222;
box-shadow: 0 4px 8px rgba(0,0,0,0.5);
padding: 20px;
position: absolute;
}
#popupMenu ul {
padding: 0px;
margin: 0px;
}
#popupMenu li {
color: #ccc;
list-style-type: none;
display: flex;
align-items: center; /* Vertically center the image and text */
padding: 5px 0; /* Add padding for better spacing */
}
#popupMenu li:hover {
background-color: #444;
cursor:pointer;
}
#popupMenu li:not(:last-child) {
border-bottom: 1px solid #444; /* Horizontal line for all li elements except the last */
}
#popupMenu li img {
margin-right: 10px; /* Space between the icon and text */
width: 20px; /* Adjust the width of the icon */
height: 20px; /* Adjust the height of the icon */
}

5
src/utils.js Normal file
View File

@ -0,0 +1,5 @@
function titleCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
export { titleCase };