Skip to content

Add drawing tools #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed assets/icons/dark/toolBrush.png
Binary file not shown.
Binary file added assets/icons/dark/toolDropper.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/dark/toolEllipse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icons/dark/toolEraser.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/dark/toolRect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/icons/light/toolBrush.png
Binary file not shown.
10 changes: 6 additions & 4 deletions assets/main/toolbar.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
<button id="zoomIn"/>
<button id="zoomOut"/>
<spacer width="100" />
<button id="toolPencil"/>
<button id="toolBrush"/>
<button id="toolFill"/>
<button id="toolEraser"/>
<button id="toolPencil" tooltip="{{toolbar.toolPencil}}" />
<button id="toolRect" tooltip="{{toolbar.toolRect}}" />
<button id="toolEllipse" tooltip="{{toolbar.toolEllipse}}" />
<button id="toolFill" tooltip="{{toolbar.toolFill}}" />
<button id="toolDropper" tooltip="{{toolbar.toolDropper}}" />
<button id="toolEraser" tooltip="{{toolbar.toolEraser}}" />
<spacer width="100"/>
<button id="musicManager"/>
<button id="settings"/>
Expand Down
37 changes: 31 additions & 6 deletions src/components/Toolbar.hx
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
package components;

import haxe.ui.containers.VBox;

@:build(haxe.ui.ComponentBuilder.build('../../assets/main/toolbar.xml'))
class Toolbar extends VBox {}
package components;

import haxe.ui.events.UIEvent;
import haxe.ui.events.MouseEvent;
import haxe.ui.containers.VBox;

using StringTools;

@:build(haxe.ui.ComponentBuilder.build('../../assets/main/toolbar.xml'))
class Toolbar extends VBox {
public function new() {
super();

toolPencil.registerEvent(MouseEvent.CLICK, onToolSelected);
toolFill.registerEvent(MouseEvent.CLICK, onToolSelected);
toolEraser.registerEvent(MouseEvent.CLICK, onToolSelected);
toolRect.registerEvent(MouseEvent.CLICK, onToolSelected);
toolEllipse.registerEvent(MouseEvent.CLICK, onToolSelected);
toolDropper.registerEvent(MouseEvent.CLICK, onToolSelected);
}

public function onToolSelected(event: MouseEvent) {
var tool = event.target.id;
if (tool.contains('tool')) {
var toolName = tool.replace('tool', '').toLowerCase();
var toolEvent = new UIEvent(MapEvent.TOOL_SELECT, false, toolName);
trace(toolEvent.data);
dispatch(toolEvent);
}
}
}
93 changes: 86 additions & 7 deletions src/components/tilemap/Tilemap.hx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ class Tilemap extends ScrollView {
public var overlay: GridQuad;
public var activeLayer(default, set): TilemapLayerData;
public var selectedTilesetCells(default, set): Array<Cell>;

public var activeTool: MapEditorTool;

var isShapeFillActive: Bool = false;
var selectedTilesetTiles: Array<TilemapTile>;
var tilesetRect: Rect;
var viewport: Visual;
Expand Down Expand Up @@ -89,6 +91,7 @@ class Tilemap extends ScrollView {
overlay.depth = 90;
overlay.grid.onGridClick(null, handleGridClick);
overlay.grid.onOnGridSelection(null, handleGridPointerMove);
overlay.onPointerUp(null, handlePointerUp);
viewport.add(overlay);
}

Expand Down Expand Up @@ -155,7 +158,7 @@ class Tilemap extends ScrollView {
}

public function onPointerMove(info: TouchInfo) {
if (tileCursor == null) {
if (tileCursor == null || !tileCursor.visible) {
return;
};
var localCoords = new Point();
Expand Down Expand Up @@ -218,6 +221,59 @@ class Tilemap extends ScrollView {
updateLayerTiles(tiles);
}

function floodFillDraw(x: Float, y: Float, initialCell: Cell, replacementTile: TilemapTile) {
var stack: Array<Point> = [];

if (!withinTilemapBounds(x, y)) {
return;
}

var activeLayerTiles = getActiveLayerTiles();
var initialTile = activeLayerTiles[initialCell.frame];

if (initialTile == replacementTile) {
return;
}

stack.push(new Point(x, y));

while (stack.length > 0) {
var currentPoint = stack.pop();
x = currentPoint.x;
y = currentPoint.y;
var currentCellIndex = overlay.grid.getCellFrame(x, y);
var currentTile = activeLayerTiles[currentCellIndex];

if (currentTile != initialTile) continue;

activeLayerTiles[currentCellIndex] = replacementTile;

stack.push({ x: x + tileSize.width, y: y });
stack.push({ x: x - tileSize.width, y: y });
stack.push({ x: x, y: y + tileSize.height });
stack.push({ x: x, y: y - tileSize.height });
}
updateLayerTiles(activeLayerTiles);
}

function drawRectShapeFill(rect: Rect) {
var activeLayerTiles = getActiveLayerTiles();
var cols = Std.int(rect.width / tileSize.width) + 1;
var rows = Std.int(rect.height / tileSize.height) + 1;

for (i in 0...rows) {
for (j in 0...cols) {
var tileX: Float = rect.x + j * tileSize.width;
var tileY: Float = rect.y + i * tileSize.height;
var currentCellIndex = overlay.grid.getCellFrame(tileX, tileY);
var tilemapTile = selectedTilesetTiles[currentCellIndex];

activeLayerTiles[currentCellIndex] = tilemapTile;
updateLayerTiles(activeLayerTiles);
}
}
}

function withinTilemapBounds(x: Float, y: Float) {
return x >= 0 && x < tilemap.width && y >= 0 && y < tilemap.height;
}
Expand All @@ -228,16 +284,35 @@ class Tilemap extends ScrollView {
new Rect(x, y, tilesetRect.width, tilesetRect.height)
);

if (buttonId == 0) {
drawTile(cellsToEdit);
if (buttonId == 0) {
switch (activeTool) {
case MapEditorTool.Pencil:
drawTile(cellsToEdit);
case MapEditorTool.Eraser:
eraseTile(cellsToEdit);
case MapEditorTool.Fill:
var replacementTile = selectedTilesetTiles[0];
floodFillDraw(x, y, selectedTilesetCells[0], replacementTile);
case MapEditorTool.Rect:
isShapeFillActive = true;
overlay.grid.enableSelection = true;
tileCursor.visible = false;
case MapEditorTool.Elipse:
case MapEditorTool.Clone:
}
} else if (buttonId == 2) {
eraseTile(cellsToEdit);
}
}

function handleGridPointerMove(cells: Array<Cell>, _) {
if (activeLayer != null && tilesetRect != null) {
handleTilemapAction(cells[0].position.x, cells[0].position.y);
function handleGridPointerMove(cells: Array<Cell>, __) {
// if (activeLayer != null && selectionRect != null) {
// handleTilemapAction(tiles[0].position.x, tiles[0].position.y);
// }
if (isShapeFillActive) {
var rect = overlay.grid.createRectFromCells(cells, tileSize);

drawRectShapeFill(rect);
}
}

Expand All @@ -250,6 +325,10 @@ class Tilemap extends ScrollView {
}

function handlePointerUp(info: TouchInfo) {
if (isShapeFillActive) {
isShapeFillActive = false;
tileCursor.visible = true;
}
if (info.buttonId == buttonId) {
buttonId = -1;
}
Expand Down
22 changes: 22 additions & 0 deletions src/constants/MapEditorTool.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package constants;

enum abstract MapEditorTool(String) from String to String {
var Pencil = 'pencil';
var Fill = 'fill';
var Rect = 'rect';
var Elipse = 'elipse';
var Eraser = 'eraser';
var Clone = 'clone';

static public function fromName(name: String): MapEditorTool {
switch (name) {
case 'pencil': return MapEditorTool.Pencil;
case Fill: return MapEditorTool.Fill;
case Rect: return MapEditorTool.Rect;
case Elipse: return MapEditorTool.Elipse;
case Eraser: return MapEditorTool.Eraser;
case Clone: return MapEditorTool.Clone;
case _: return MapEditorTool.Pencil;
}
}
}
3 changes: 3 additions & 0 deletions src/constants/MapEvent.hx
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ class MapEvent extends UIEvent {

/* Dispatched when the tilemap is zoomed' */
public static final MAP_ZOOM: EventType<UIEvent> = EventType.name('mapZoom');

/* Dispatched when a tool is selected */
public static final TOOL_SELECT: EventType<UIEvent> = EventType.name('toolChange');
}
7 changes: 7 additions & 0 deletions src/locale/en_US/toolbar.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Drawing Tools
toolbar.toolPencil=Pencil
toolbar.toolRect=Rect
toolbar.toolFill=Fill
toolbar.toolEllipse=Ellipse
toolbar.toolDropper=Eye Dropper
toolbar.toolEraser=Eraser
1 change: 1 addition & 0 deletions src/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<locale id="en_US">
<resource path="locale/en_US/menus.properties" />
<resource path="locale/en_US/layerList.properties" />
<resource path="locale/en_US/toolbar.properties" />
</locale>
</locales>

Expand Down
10 changes: 8 additions & 2 deletions src/styles/light/toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,18 @@
.toolbar #toolPencil {
icon: icon('toolPencil');
}
.toolbar #toolBrush {
icon: icon('toolBrush');
.toolbar #toolRect {
icon: icon('toolRect');
}
.toolbar #toolEllipse {
icon: icon('toolEllipse');
}
.toolbar #toolFill {
icon: icon('toolFill');
}
.toolbar #toolDropper {
icon: icon('toolDropper');
}
.toolbar #toolEraser {
icon: icon('toolEraser');
}
Expand Down
5 changes: 5 additions & 0 deletions src/views/MapEditor.hx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class MapEditor extends VBox {
layerPanel.registerEvent(MapEvent.LAYER_RENAME, onLayerRename);
mapListPanel.registerEvent(MapEvent.MAP_SELECT, onActiveMapChanged);
tilePicker.registerEvent(MapEvent.TILESET_TILE_SELECTION, onTileSelection);
toolbar.registerEvent(MapEvent.TOOL_SELECT, onToolSelect);
}

public function menu(): Array<ContextMenuEntry> {
Expand Down Expand Up @@ -143,6 +144,10 @@ class MapEditor extends VBox {
layerPanel.activeLayer.name = event.data;
}

function onToolSelect(event: UIEvent) {
tilemapView.activeTool = MapEditorTool.fromName(event.data);
}

public function update(dt: Float) {}

function emptyTilemapData(name: String) {
Expand Down