Skip to content

Commit c3e4f17

Browse files
committed
Fix issue #7: Create an Ellipse tool. This commit shows that there are too many different places to register things for a new tool: A Tool subclass, an Action, a Toolbar button, a custom element, registering the custom element, a key binding, and handles to the new Tool in the editor.
1 parent cd9e05d commit c3e4f17

8 files changed

Lines changed: 95 additions & 4 deletions

File tree

src/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const CarveAction = {
1313

1414
// Drawing Actions.
1515
RECTANGLE_MODE: 'rectangle_mode',
16+
ELLIPSE_MODE: 'ellipse_mode',
1617
}
1718

1819
// It is confusing to have a type the same name as the above object, but Typescript allows this.

src/carve.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { CarveNewButton, CarveOpenButton, CarveRectangleButton } from './core-toolbar-buttons.js';
1+
import { CarveEllipseButton, CarveNewButton, CarveOpenButton, CarveRectangleButton } from './core-toolbar-buttons.js';
22
import { CarveEditor } from './editor.js';
33

44
customElements.define('carve-editor', CarveEditor);
55
customElements.define('carve-new-button', CarveNewButton);
66
customElements.define('carve-open-button', CarveOpenButton);
77
customElements.define('carve-rectangle-button', CarveRectangleButton);
8+
customElements.define('carve-ellipse-button', CarveEllipseButton);
89

910
console.log(`Carve custom elements registered.`);

src/core-toolbar-buttons.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,13 @@ export class CarveRectangleButton extends ToolbarModeButton {
3737
</svg>`;
3838
}
3939
}
40+
41+
export class CarveEllipseButton extends ToolbarModeButton {
42+
getAction(): CarveAction { return CarveAction.ELLIPSE_MODE; }
43+
getButtonDOM(): string {
44+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
45+
<title>Rectangle Tool</title>
46+
<ellipse cx="50" cy="50" rx="40" ry="20" fill="green" stroke-width="4" stroke="black" />
47+
</svg>`;
48+
}
49+
}

src/editor.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CarveAction } from './actions.js';
22
import { CarveDocument, createDocumentFromFile, createNewDocument } from './document.js';
33
import { CarveMouseEvent } from './carve-mouse-event.js';
4-
import { CarveRectangleButton } from './core-toolbar-buttons.js';
4+
import { CarveRectangleButton, CarveEllipseButton } from './core-toolbar-buttons.js';
55
import { Command } from './commands/command.js';
66
import { EditorHost } from './editor-host.js';
77
import { FileSystemFileHandle } from './types/filesystem.js';
@@ -10,6 +10,7 @@ import { SVGNS } from './constants.js';
1010
import { Tool } from './tools/tool.js';
1111
import { ToolbarClickedEvent, TOOLBAR_CLICKED_TYPE } from './toolbar-button.js';
1212
import { keyToAction } from './keys.js';
13+
import { EllipseTool } from './tools/ellipse.js';
1314

1415
const CARVE_TOP_DIV = 'carveTopDiv';
1516
const CARVE_WORK_AREA = 'carveWorkArea';
@@ -32,13 +33,15 @@ export class CarveEditor extends HTMLElement implements EditorHost {
3233

3334
// Known tools.
3435
private rectTool: RectangleTool;
36+
private ellipseTool: EllipseTool;
3537

3638
constructor() {
3739
super();
3840
this.createShadowDOM();
3941

4042
// Set up tools.
4143
this.rectTool = new RectangleTool(this);
44+
this.ellipseTool = new EllipseTool(this);
4245

4346
// Listen for events.
4447
window.addEventListener('keyup', this);
@@ -87,6 +90,10 @@ export class CarveEditor extends HTMLElement implements EditorHost {
8790
(this.querySelector('carve-rectangle-button') as CarveRectangleButton).active = true;
8891
this.currentModeTool = this.rectTool;
8992
break;
93+
case CarveAction.ELLIPSE_MODE:
94+
(this.querySelector('carve-ellipse-button') as CarveEllipseButton).active = true;
95+
this.currentModeTool = this.ellipseTool;
96+
break;
9097
}
9198
}
9299
}

src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<carve-new-button slot="toolbar"></carve-new-button>
1414
<carve-open-button slot="toolbar"></carve-open-button>
1515
<carve-rectangle-button slot="toolbar"></carve-rectangle-button>
16+
<carve-ellipse-button slot="toolbar"></carve-ellipse-button>
1617
</carve-editor>
1718
</body>
1819
</html>

src/keys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export function keyToAction(key: string): CarveAction {
88
case 'o': return CarveAction.OPEN_DOCUMENT;
99
// Drawing modes.
1010
case 'r': return CarveAction.RECTANGLE_MODE;
11+
case 'e': return CarveAction.ELLIPSE_MODE;
1112
}
1213

1314
return null;

src/tools/ellipse.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { EditorHost } from '../editor-host.js';
2+
import { Point } from '../math/point.js';
3+
import { SVGNS } from '../constants.js';
4+
import { Tool } from './tool.js';
5+
import { InsertElementCommand } from '../commands/insert-element-command.js';
6+
import { CarveMouseEvent } from '../carve-mouse-event.js';
7+
8+
/** A tool for drawing an ellipse. */
9+
export class EllipseTool extends Tool {
10+
private isDrawing: boolean = false;
11+
private startPoint: Point;
12+
private endPoint: Point;
13+
private drawingElem: SVGEllipseElement;
14+
15+
constructor(host: EditorHost) {
16+
super(host);
17+
}
18+
19+
onMouseDown(evt: CarveMouseEvent) {
20+
this.isDrawing = true;
21+
this.startPoint = new Point(evt.carveX, evt.carveY);
22+
this.endPoint = new Point(evt.carveX, evt.carveY);
23+
24+
const elem = document.createElementNS(SVGNS, 'ellipse');
25+
elem.setAttribute('cx', `${evt.carveX}`);
26+
elem.setAttribute('cy', `${evt.carveY}`);
27+
elem.setAttribute('fill', 'green');
28+
elem.setAttribute('stroke-width', '1');
29+
elem.setAttribute('stroke', 'black');
30+
31+
this.drawingElem = elem;
32+
this.host.getOverlay().appendChild(elem);
33+
console.log(`EllipseTool: Started creating an ellipse`);
34+
}
35+
36+
onMouseUp(evt: CarveMouseEvent) {
37+
if (this.isDrawing) {
38+
this.isDrawing = false;
39+
this.endPoint = new Point(evt.carveX, evt.carveY);
40+
41+
// Do not create a rect if it would be zero width/height.
42+
if (this.startPoint.x !== this.endPoint.x && this.startPoint.y !== this.endPoint.y) {
43+
this.host.execute(new InsertElementCommand(this.drawingElem));
44+
console.log(`EllipseTool: Created an ellipse`);
45+
} else {
46+
this.drawingElem.parentElement.removeChild(this.drawingElem);
47+
console.log(`EllipseTool: Abandoned creating an ellipse`);
48+
}
49+
50+
this.host.getOverlay().innerHTML = '';
51+
this.cleanUp();
52+
}
53+
}
54+
55+
onMouseMove(evt: CarveMouseEvent) {
56+
if (this.isDrawing) {
57+
this.endPoint.x = evt.carveX;
58+
this.endPoint.y = evt.carveY;
59+
this.drawingElem.setAttribute('rx', `${Math.abs(this.endPoint.x - this.startPoint.x)}`);
60+
this.drawingElem.setAttribute('ry', `${Math.abs(this.endPoint.y - this.startPoint.y)}`);
61+
}
62+
}
63+
64+
private cleanUp() {
65+
this.isDrawing = false;
66+
this.startPoint = null;
67+
this.endPoint = null;
68+
this.drawingElem = null;
69+
}
70+
}

src/tools/rectangle.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export class RectangleTool extends Tool {
2525
elem.setAttribute('x', `${evt.carveX}`);
2626
elem.setAttribute('y', `${evt.carveY}`);
2727
elem.setAttribute('fill', 'red');
28+
elem.setAttribute('stroke-width', '1');
29+
elem.setAttribute('stroke', 'black');
2830

2931
this.drawingElem = elem;
3032
this.host.getOverlay().appendChild(elem);
@@ -60,8 +62,6 @@ export class RectangleTool extends Tool {
6062
`${(this.endPoint.x < this.startPoint.x) ? this.endPoint.x : this.startPoint.x}`);
6163
this.drawingElem.setAttribute('y',
6264
`${(this.endPoint.y < this.startPoint.y) ? this.endPoint.y : this.startPoint.y}`);
63-
this.drawingElem.setAttribute('stroke-width', '1');
64-
this.drawingElem.setAttribute('stroke', 'black');
6565
}
6666
}
6767

0 commit comments

Comments
 (0)