forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
175
lib/leenkx2d/Sources/lnx2d/Assets.hx
Normal file
175
lib/leenkx2d/Sources/lnx2d/Assets.hx
Normal file
@ -0,0 +1,175 @@
|
||||
package lnx2d;
|
||||
|
||||
// Zui
|
||||
import leenkx.ui.Canvas;
|
||||
|
||||
// Editor
|
||||
import lnx2d.Path;
|
||||
import lnx2d.ui.UIProperties;
|
||||
|
||||
class Assets {
|
||||
|
||||
public static function getImage(asset:TAsset):kha.Image {
|
||||
return Canvas.assetMap.get(asset.id);
|
||||
}
|
||||
|
||||
public static function getFont(asset:TAsset):kha.Font {
|
||||
return Canvas.assetMap.get(asset.id);
|
||||
}
|
||||
|
||||
public static function importAsset(canvas:TCanvas, path:String) {
|
||||
var abspath = Path.toAbsolute(path, Main.cwd);
|
||||
abspath = kha.System.systemId == "Windows" ? StringTools.replace(abspath, "/", "\\") : abspath;
|
||||
|
||||
if (isPathImage(path)) {
|
||||
kha.Assets.loadImageFromPath(abspath, false, function(image:kha.Image) {
|
||||
var ar = path.split("/");
|
||||
var name = ar[ar.length - 1];
|
||||
var asset:TAsset = { name: name, file: path, id: Canvas.getAssetId(canvas) };
|
||||
canvas.assets.push(asset);
|
||||
Canvas.assetMap.set(asset.id, image);
|
||||
|
||||
Editor.assetNames.push(name);
|
||||
UIProperties.hwin.redraws = 2;
|
||||
});
|
||||
}
|
||||
else if (isPathFont(path)) {
|
||||
kha.Assets.loadFontFromPath(abspath, function(font:kha.Font) {
|
||||
var ar = path.split("/");
|
||||
var name = ar[ar.length - 1];
|
||||
var asset:TAsset = { name: name, file: path, id: Canvas.getAssetId(canvas) };
|
||||
canvas.assets.push(asset);
|
||||
Canvas.assetMap.set(asset.id, font);
|
||||
|
||||
Editor.assetNames.push(name);
|
||||
UIProperties.hwin.redraws = 2;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports all themes from '_themes.json'. If the file doesn't exist, the
|
||||
* default light theme is used instead.
|
||||
*/
|
||||
public static function importThemes() {
|
||||
var themesDir = haxe.io.Path.directory(Main.prefs.path);
|
||||
var themesPath = haxe.io.Path.join([themesDir, "_themes.json"]);
|
||||
themesPath = kha.System.systemId == "Windows" ? StringTools.replace(themesPath, "/", "\\") : themesPath;
|
||||
|
||||
try {
|
||||
kha.Assets.loadBlobFromPath(themesPath, function(b:kha.Blob) {
|
||||
Canvas.themes = haxe.Json.parse(b.toString());
|
||||
|
||||
if (Canvas.themes.length == 0) {
|
||||
Canvas.themes.push(Reflect.copy(leenkx.ui.Themes.light));
|
||||
}
|
||||
if (Main.inst != null) Editor.selectedTheme = Canvas.themes[0];
|
||||
|
||||
// Error handling for HTML5 target
|
||||
}, function(a:kha.AssetError) {
|
||||
Canvas.themes.push(Reflect.copy(leenkx.ui.Themes.light));
|
||||
if (Main.inst != null) Editor.selectedTheme = Canvas.themes[0];
|
||||
});
|
||||
}
|
||||
// Error handling for Krom, as the failed callback for loadBlobFromPath()
|
||||
// is currently not implemented in Krom
|
||||
catch (e: Dynamic) {
|
||||
Canvas.themes.push(Reflect.copy(leenkx.ui.Themes.light));
|
||||
if(Main.inst != null) Editor.selectedTheme = Canvas.themes[0];
|
||||
}
|
||||
}
|
||||
|
||||
public static function save(canvas: TCanvas) {
|
||||
// Unpan
|
||||
canvas.x = 0;
|
||||
canvas.y = 0;
|
||||
|
||||
saveCanvas(canvas);
|
||||
saveAssets(canvas);
|
||||
saveThemes();
|
||||
|
||||
canvas.x = Editor.coffX;
|
||||
canvas.y = Editor.coffY;
|
||||
}
|
||||
|
||||
public static function load(done: TCanvas->Void) {
|
||||
kha.Assets.loadBlobFromPath(Main.prefs.path, function(b: kha.Blob) {
|
||||
done(Canvas.parseCanvasFromBlob(b));
|
||||
});
|
||||
}
|
||||
|
||||
static function saveCanvas(canvas: TCanvas) {
|
||||
#if kha_krom
|
||||
Krom.fileSaveBytes(Main.prefs.path, haxe.io.Bytes.ofString(haxe.Json.stringify(canvas)).getData());
|
||||
|
||||
#elseif kha_debug_html5
|
||||
html5WriteFile(Main.prefs.path, haxe.Json.stringify(canvas));
|
||||
#end
|
||||
}
|
||||
|
||||
static function saveAssets(canvas: TCanvas) {
|
||||
var filesPath = Main.prefs.path.substr(0, Main.prefs.path.length - 5); // .json
|
||||
filesPath += '.files';
|
||||
|
||||
var filesList = '';
|
||||
for (a in canvas.assets) filesList += a.file + '\n';
|
||||
|
||||
#if kha_krom
|
||||
Krom.fileSaveBytes(filesPath, haxe.io.Bytes.ofString(filesList).getData());
|
||||
|
||||
#elseif kha_debug_html5
|
||||
html5WriteFile(filesPath, filesList);
|
||||
#end
|
||||
}
|
||||
|
||||
static function saveThemes() {
|
||||
var themesPath = haxe.io.Path.join([haxe.io.Path.directory(Main.prefs.path), "_themes.json"]);
|
||||
|
||||
#if kha_krom
|
||||
Krom.fileSaveBytes(themesPath, haxe.io.Bytes.ofString(haxe.Json.stringify(Canvas.themes)).getData());
|
||||
|
||||
#elseif kha_debug_html5
|
||||
html5WriteFile(themesPath, haxe.Json.stringify(Canvas.themes));
|
||||
#end
|
||||
}
|
||||
|
||||
#if kha_debug_html5
|
||||
static function html5WriteFile(filePath: String, data: String) {
|
||||
var fs = js.Syntax.code('require("fs");');
|
||||
var path = js.Syntax.code('require("path")');
|
||||
|
||||
var filePath = path.resolve(js.Syntax.code('__dirname'), filePath);
|
||||
|
||||
try { fs.writeFileSync(filePath, data); }
|
||||
catch (x: Dynamic) { trace('saving "${filePath}" failed'); }
|
||||
}
|
||||
#end
|
||||
|
||||
public static function getEnumTexts():Array<String> {
|
||||
if(Main.inst==null) return [""];
|
||||
return Editor.assetNames.length > 0 ? Editor.assetNames : [""];
|
||||
}
|
||||
|
||||
public static function getAssetIndex(canvas:TCanvas, asset:String):Int {
|
||||
for (i in 0...canvas.assets.length) if (asset == canvas.assets[i].name) return i + 1; // assetNames[0] = ""
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given path is a path to an image file.
|
||||
* @param path The path of the asset
|
||||
* @return Bool
|
||||
*/
|
||||
public static function isPathImage(path: String): Bool {
|
||||
var extension = haxe.io.Path.extension(path).toLowerCase();
|
||||
return extension == "jpg" || extension == "png" || extension == "k" || extension == "hdr";
|
||||
}
|
||||
/**
|
||||
* Returns if the given path is a path to a font file.
|
||||
* @param path The path of the asset
|
||||
* @return Bool
|
||||
*/
|
||||
public static inline function isPathFont(path: String): Bool {
|
||||
return haxe.io.Path.extension(path).toLowerCase() == "ttf";
|
||||
}
|
||||
}
|
470
lib/leenkx2d/Sources/lnx2d/Editor.hx
Normal file
470
lib/leenkx2d/Sources/lnx2d/Editor.hx
Normal file
@ -0,0 +1,470 @@
|
||||
package lnx2d;
|
||||
|
||||
// Zui
|
||||
import zui.Zui;
|
||||
import zui.Themes;
|
||||
import zui.Id;
|
||||
using zui.Ext;
|
||||
import leenkx.ui.Popup;
|
||||
import leenkx.ui.Canvas;
|
||||
|
||||
// Editor
|
||||
import lnx2d.Path;
|
||||
import lnx2d.Assets;
|
||||
import lnx2d.tools.Math;
|
||||
import lnx2d.ui.UIToolBar;
|
||||
import lnx2d.ui.UIProperties;
|
||||
import lnx2d.tools.CanvasTools;
|
||||
|
||||
@:access(zui.Zui)
|
||||
class Editor {
|
||||
var ui:Zui;
|
||||
public var cui:Zui;
|
||||
public var canvas:TCanvas;
|
||||
|
||||
public static var defaultWindowW = 240;
|
||||
public static var windowW = defaultWindowW;
|
||||
static var uiw(get, null):Int;
|
||||
static function get_uiw():Int {
|
||||
return Std.int(windowW * Main.prefs.scaleFactor);
|
||||
}
|
||||
var toolbarw(get, null):Int;
|
||||
function get_toolbarw():Int {
|
||||
return Std.int(140 * ui.SCALE());
|
||||
}
|
||||
|
||||
// Canvas offset from the editor window
|
||||
// Should be a multiple of gridSize to ensure visual grid alignment
|
||||
public static var coffX = 160.0;
|
||||
public static var coffY = 40.0;
|
||||
|
||||
var dropPath = "";
|
||||
public static var currentOperation = "";
|
||||
public static var assetNames:Array<String> = [""];
|
||||
public static var dragAsset:TAsset = null;
|
||||
var resizeCanvas = false;
|
||||
var zoom = 1.0;
|
||||
|
||||
public static var showFiles = false;
|
||||
public static var foldersOnly = false;
|
||||
public static var filesDone:String->Void = null;
|
||||
var uimodal:Zui;
|
||||
|
||||
public static var gridSnapBounds:Bool = false;
|
||||
public static var gridSnapPos:Bool = true;
|
||||
public static var gridUseRelative:Bool = true;
|
||||
public static var useRotationSteps:Bool = false;
|
||||
public static var rotationSteps:Float = Math.toRadians(15);
|
||||
public static var gridSize:Int = 20;
|
||||
public static var redrawGrid = false;
|
||||
static var grid:kha.Image = null;
|
||||
static var timeline:kha.Image = null;
|
||||
|
||||
var selectedFrame = 0;
|
||||
public static var selectedTheme:zui.Themes.TTheme = null;
|
||||
public static var selectedElem:TElement = null;
|
||||
var lastW = 0;
|
||||
var lastH = 0;
|
||||
var lastCanvasW = 0;
|
||||
var lastCanvasH = 0;
|
||||
|
||||
public function new(canvas:TCanvas) {
|
||||
this.canvas = canvas;
|
||||
|
||||
// Reimport assets
|
||||
if (canvas.assets.length > 0) {
|
||||
var assets = canvas.assets;
|
||||
canvas.assets = [];
|
||||
for (a in assets) Assets.importAsset(canvas, a.file);
|
||||
}
|
||||
|
||||
Assets.importThemes();
|
||||
|
||||
kha.Assets.loadEverything(loaded);
|
||||
}
|
||||
|
||||
function loaded() {
|
||||
var t = Reflect.copy(Themes.dark);
|
||||
t.FILL_WINDOW_BG = true;
|
||||
ui = new Zui({scaleFactor: Main.prefs.scaleFactor, font: kha.Assets.fonts.font_default, theme: t, color_wheel: kha.Assets.images.color_wheel, black_white_gradient: kha.Assets.images.black_white_gradient});
|
||||
cui = new Zui({scaleFactor: 1.0, font: kha.Assets.fonts.font_default, autoNotifyInput: true, theme: Reflect.copy(Canvas.getTheme(canvas.theme))});
|
||||
uimodal = new Zui( { font: kha.Assets.fonts.font_default, scaleFactor: Main.prefs.scaleFactor } );
|
||||
ElementController.initialize(ui, cui);
|
||||
|
||||
if (Canvas.getTheme(canvas.theme) == null) {
|
||||
Popup.showMessage(new Zui(ui.ops), "Warning!",
|
||||
'Theme "${canvas.theme}" was not found!'
|
||||
+ '\nUsing first theme in list instead: "${Canvas.themes[0].NAME}"');
|
||||
canvas.theme = Canvas.themes[0].NAME;
|
||||
}
|
||||
|
||||
kha.System.notifyOnDropFiles(function(path:String) {
|
||||
dropPath = StringTools.rtrim(path);
|
||||
dropPath = Path.toRelative(dropPath, Main.cwd);
|
||||
});
|
||||
|
||||
kha.System.notifyOnFrames(onFrames);
|
||||
kha.Scheduler.addTimeTask(update, 0, 1 / 60);
|
||||
}
|
||||
|
||||
function resize() {
|
||||
if (grid != null) {
|
||||
grid.unload();
|
||||
grid = null;
|
||||
}
|
||||
if (timeline != null) {
|
||||
timeline.unload();
|
||||
timeline = null;
|
||||
}
|
||||
}
|
||||
|
||||
function drawGrid() {
|
||||
redrawGrid = false;
|
||||
|
||||
var scaledGridSize = scaled(gridSize);
|
||||
var doubleGridSize = scaled(gridSize * 2);
|
||||
|
||||
var ww = kha.System.windowWidth();
|
||||
var wh = kha.System.windowHeight();
|
||||
var w = ww + doubleGridSize * 2;
|
||||
var h = wh + doubleGridSize * 2;
|
||||
|
||||
if (grid == null) {
|
||||
grid = kha.Image.createRenderTarget(w, h);
|
||||
}
|
||||
grid.g2.begin(true, 0xff242424);
|
||||
|
||||
for (i in 0...Std.int(h / doubleGridSize) + 1) {
|
||||
grid.g2.color = 0xff282828;
|
||||
grid.g2.drawLine(0, i * doubleGridSize + scaledGridSize, w, i * doubleGridSize + scaledGridSize);
|
||||
grid.g2.color = 0xff323232;
|
||||
grid.g2.drawLine(0, i * doubleGridSize, w, i * doubleGridSize);
|
||||
}
|
||||
for (i in 0...Std.int(w / doubleGridSize) + 1) {
|
||||
grid.g2.color = 0xff282828;
|
||||
grid.g2.drawLine(i * doubleGridSize + scaledGridSize, 0, i * doubleGridSize + scaledGridSize, h);
|
||||
grid.g2.color = 0xff323232;
|
||||
grid.g2.drawLine(i * doubleGridSize, 0, i * doubleGridSize, h);
|
||||
}
|
||||
|
||||
grid.g2.end();
|
||||
}
|
||||
|
||||
function drawTimeline(timelineLabelsHeight:Int, timelineFramesHeight:Int) {
|
||||
var sc = ui.SCALE();
|
||||
|
||||
var timelineHeight = timelineLabelsHeight + timelineFramesHeight;
|
||||
|
||||
timeline = kha.Image.createRenderTarget(kha.System.windowWidth() - uiw - toolbarw, timelineHeight);
|
||||
|
||||
var g = timeline.g2;
|
||||
g.begin(true, 0xff222222);
|
||||
g.font = kha.Assets.fonts.font_default;
|
||||
g.fontSize = Std.int(16 * sc);
|
||||
|
||||
// Labels
|
||||
var frames = Std.int(timeline.width / (11 * sc));
|
||||
for (i in 0...Std.int(frames / 5) + 1) {
|
||||
var frame = i * 5;
|
||||
|
||||
var frameTextWidth = kha.Assets.fonts.font_default.width(g.fontSize, frame + "");
|
||||
g.drawString(frame + "", i * 55 * sc + 5 * sc - frameTextWidth / 2, timelineLabelsHeight / 2 - g.fontSize / 2);
|
||||
}
|
||||
|
||||
// Frames
|
||||
for (i in 0...frames) {
|
||||
g.color = i % 5 == 0 ? 0xff444444 : 0xff333333;
|
||||
g.fillRect(i * 11 * sc, timelineHeight - timelineFramesHeight, 10 * sc, timelineFramesHeight);
|
||||
}
|
||||
|
||||
g.end();
|
||||
}
|
||||
|
||||
public function onFrames(framebuffers: Array<kha.Framebuffer>): Void {
|
||||
// Prevent crash when minimizing window
|
||||
if (kha.System.windowWidth() == 0 || kha.System.windowHeight() == 0) return;
|
||||
|
||||
var framebuffer = framebuffers[0];
|
||||
|
||||
// Disable UI if a popup is displayed
|
||||
if (Popup.show && ui.inputRegistered) {
|
||||
ui.unregisterInput();
|
||||
cui.unregisterInput();
|
||||
} else if (!Popup.show && !ui.inputRegistered) {
|
||||
ui.registerInput();
|
||||
cui.registerInput();
|
||||
}
|
||||
|
||||
// Update preview when choosing a color
|
||||
if (Popup.show) UIProperties.hwin.redraws = 1;
|
||||
|
||||
if (dropPath != "") {
|
||||
Assets.importAsset(canvas, dropPath);
|
||||
dropPath = "";
|
||||
}
|
||||
|
||||
var sc = ui.SCALE();
|
||||
var timelineLabelsHeight = Std.int(30 * sc);
|
||||
var timelineFramesHeight = Std.int(40 * sc);
|
||||
|
||||
// Bake and redraw if the UI scale has changed
|
||||
if (grid == null || redrawGrid) drawGrid();
|
||||
if (timeline == null || timeline.height != timelineLabelsHeight + timelineFramesHeight) drawTimeline(timelineLabelsHeight, timelineFramesHeight);
|
||||
|
||||
var g = framebuffer.g2;
|
||||
g.begin();
|
||||
|
||||
g.color = 0xffffffff;
|
||||
var doubleGridSize = scaled(gridSize * 2);
|
||||
g.drawImage(grid, coffX % doubleGridSize - doubleGridSize, coffY % doubleGridSize - doubleGridSize);
|
||||
|
||||
// Canvas outline
|
||||
canvas.x = coffX;
|
||||
canvas.y = coffY;
|
||||
g.drawRect(canvas.x, canvas.y, scaled(canvas.width), scaled(canvas.height), 1.0);
|
||||
|
||||
// Canvas resize
|
||||
var handleSize = ElementController.handleSize;
|
||||
if (Math.hitbox(cui, canvas.x + scaled(canvas.width) - handleSize / 2, canvas.y + scaled(canvas.height) - handleSize / 2, handleSize, handleSize)) {
|
||||
g.color = 0xff205d9c;
|
||||
g.fillRect(canvas.x + scaled(canvas.width) - handleSize / 2, canvas.y + scaled(canvas.height) - handleSize / 2, handleSize, handleSize);
|
||||
g.color = 0xffffffff;
|
||||
}
|
||||
g.drawRect(canvas.x + scaled(canvas.width) - handleSize / 2, canvas.y + scaled(canvas.height) - handleSize / 2, handleSize, handleSize, 1);
|
||||
|
||||
Canvas.screenW = canvas.width;
|
||||
Canvas.screenH = canvas.height;
|
||||
Canvas.draw(cui, canvas, g);
|
||||
|
||||
ElementController.render(g, canvas);
|
||||
|
||||
if (currentOperation != "") {
|
||||
g.fontSize = Std.int(14 * ui.SCALE());
|
||||
g.color = 0xffaaaaaa;
|
||||
g.drawString(currentOperation, toolbarw, kha.System.windowHeight() - timeline.height - g.fontSize);
|
||||
}
|
||||
|
||||
// Timeline
|
||||
var showTimeline = true;
|
||||
if (showTimeline) {
|
||||
g.color = 0xffffffff;
|
||||
var ty = kha.System.windowHeight() - timeline.height;
|
||||
g.drawImage(timeline, toolbarw, ty);
|
||||
|
||||
g.color = 0xff205d9c;
|
||||
g.fillRect(toolbarw + selectedFrame * 11 * sc, ty + timelineLabelsHeight, 10 * sc, timelineFramesHeight);
|
||||
|
||||
// Show selected frame number
|
||||
g.font = kha.Assets.fonts.font_default;
|
||||
g.fontSize = Std.int(16 * sc);
|
||||
|
||||
var frameIndicatorMargin = 4 * sc;
|
||||
var frameIndicatorPadding = 4 * sc;
|
||||
var frameIndicatorWidth = 30 * sc;
|
||||
var frameIndicatorHeight = timelineLabelsHeight - frameIndicatorMargin * 2;
|
||||
var frameTextWidth = kha.Assets.fonts.font_default.width(g.fontSize, "" + selectedFrame);
|
||||
|
||||
// Scale the indicator if the contained text is too long
|
||||
if (frameTextWidth > frameIndicatorWidth + frameIndicatorPadding) {
|
||||
frameIndicatorWidth = frameTextWidth + frameIndicatorPadding;
|
||||
}
|
||||
|
||||
g.fillRect(toolbarw + selectedFrame * 11 * sc + 5 * sc - frameIndicatorWidth / 2, ty + frameIndicatorMargin, frameIndicatorWidth, frameIndicatorHeight);
|
||||
g.color = 0xffffffff;
|
||||
g.drawString("" + selectedFrame, toolbarw + selectedFrame * 11 * sc + 5 * sc - frameTextWidth / 2, ty + timelineLabelsHeight / 2 - g.fontSize / 2);
|
||||
}
|
||||
|
||||
g.end();
|
||||
|
||||
ui.begin(g);
|
||||
|
||||
UIToolBar.renderToolbar(ui, cui, canvas, toolbarw);
|
||||
|
||||
if (ui.window(Id.handle(), toolbarw, 0, kha.System.windowWidth() - uiw - toolbarw, Std.int((ui.t.ELEMENT_H + 2) * ui.SCALE()))) {
|
||||
ui.tab(Id.handle(), canvas.name);
|
||||
}
|
||||
|
||||
UIProperties.renderProperties(ui, uiw, canvas);
|
||||
|
||||
ui.end();
|
||||
|
||||
if (ui.changed && !ui.inputDown) drawGrid();
|
||||
|
||||
g.begin(false);
|
||||
|
||||
if (dragAsset != null) {
|
||||
var w = std.Math.min(128, Assets.getImage(dragAsset).width);
|
||||
var ratio = w / Assets.getImage(dragAsset).width;
|
||||
var h = Assets.getImage(dragAsset).height * ratio;
|
||||
g.drawScaledImage(Assets.getImage(dragAsset), ui.inputX, ui.inputY, w, h);
|
||||
}
|
||||
|
||||
g.end();
|
||||
|
||||
if (lastW > 0 && (lastW != kha.System.windowWidth() || lastH != kha.System.windowHeight())) {
|
||||
resize();
|
||||
}
|
||||
else if (lastCanvasW > 0 && (lastCanvasW != canvas.width || lastCanvasH != canvas.height)) {
|
||||
resize();
|
||||
}
|
||||
lastW = kha.System.windowWidth();
|
||||
lastH = kha.System.windowHeight();
|
||||
lastCanvasW = canvas.width;
|
||||
lastCanvasH = canvas.height;
|
||||
|
||||
if (showFiles) renderFiles(g);
|
||||
if (Popup.show) Popup.render(g);
|
||||
}
|
||||
|
||||
function acceptDrag(index:Int) {
|
||||
var elem = CanvasTools.makeElem(cui, canvas, ElementType.Image);
|
||||
elem.asset = assetNames[index + 1]; // assetNames[0] == ""
|
||||
elem.x = ui.inputX - canvas.x;
|
||||
elem.y = ui.inputY - canvas.y;
|
||||
elem.width = Assets.getImage(canvas.assets[index]).width;
|
||||
elem.height = Assets.getImage(canvas.assets[index]).height;
|
||||
selectedElem = elem;
|
||||
}
|
||||
|
||||
public function update() {
|
||||
|
||||
// Drag from assets panel
|
||||
if (ui.inputReleased && dragAsset != null) {
|
||||
if (ui.inputX < kha.System.windowWidth() - uiw) {
|
||||
var index = 0;
|
||||
for (i in 0...canvas.assets.length) if (canvas.assets[i] == dragAsset) { index = i; break; }
|
||||
acceptDrag(index);
|
||||
}
|
||||
dragAsset = null;
|
||||
}
|
||||
if (dragAsset != null) return;
|
||||
|
||||
updateCanvas();
|
||||
|
||||
// Select frame
|
||||
if (timeline != null) {
|
||||
var ty = kha.System.windowHeight() - timeline.height;
|
||||
if (ui.inputDown && ui.inputY > ty && ui.inputX < kha.System.windowWidth() - uiw && ui.inputX > toolbarw) {
|
||||
selectedFrame = Std.int((ui.inputX - toolbarw) / 11 / ui.SCALE());
|
||||
}
|
||||
}
|
||||
|
||||
ElementController.update(ui, cui, canvas);
|
||||
|
||||
if (Popup.show) Popup.update();
|
||||
|
||||
updateFiles();
|
||||
}
|
||||
|
||||
function updateCanvas() {
|
||||
if (showFiles || ui.inputX > kha.System.windowWidth() - uiw) return;
|
||||
|
||||
ElementController.selectElement(canvas);
|
||||
|
||||
if (!ElementController.isManipulating) {
|
||||
// Pan canvas
|
||||
if (ui.inputDownR) {
|
||||
coffX += Std.int(ui.inputDX);
|
||||
coffY += Std.int(ui.inputDY);
|
||||
}
|
||||
|
||||
// Zoom canvas
|
||||
if (ui.inputWheelDelta != 0) {
|
||||
var prevZoom = zoom;
|
||||
zoom += -ui.inputWheelDelta / 10;
|
||||
if (zoom < 0.4) zoom = 0.4;
|
||||
else if (zoom > 1.0) zoom = 1.0;
|
||||
zoom = std.Math.round(zoom * 10) / 10;
|
||||
cui.setScale(zoom);
|
||||
|
||||
// Update the grid only when necessary, this prevents lag from scrolling too fast
|
||||
if (prevZoom != zoom) {
|
||||
drawGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Canvas resize
|
||||
var handleSize = ElementController.handleSize;
|
||||
if (ui.inputStarted && Math.hitbox(cui, canvas.x + scaled(canvas.width) - handleSize / 2, canvas.y + scaled(canvas.height) - handleSize / 2, handleSize, handleSize)) {
|
||||
resizeCanvas = true;
|
||||
}
|
||||
if (ui.inputReleased && resizeCanvas) {
|
||||
resizeCanvas = false;
|
||||
}
|
||||
if (resizeCanvas) {
|
||||
canvas.width += Std.int(ui.inputDX);
|
||||
canvas.height += Std.int(ui.inputDY);
|
||||
if (canvas.width < 1) canvas.width = 1;
|
||||
if (canvas.height < 1) canvas.height = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function updateFiles() {
|
||||
if (!showFiles) return;
|
||||
|
||||
if (ui.inputReleased) {
|
||||
var appw = kha.System.windowWidth();
|
||||
var apph = kha.System.windowHeight();
|
||||
var left = appw / 2 - modalRectW / 2;
|
||||
var right = appw / 2 + modalRectW / 2;
|
||||
var top = apph / 2 - modalRectH / 2;
|
||||
var bottom = apph / 2 + modalRectH / 2;
|
||||
if (ui.inputX < left || ui.inputX > right || ui.inputY < top + modalHeaderH || ui.inputY > bottom) {
|
||||
showFiles = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static var modalW = 625;
|
||||
static var modalH = 545;
|
||||
static var modalHeaderH = 66;
|
||||
static var modalRectW = 625; // No shadow
|
||||
static var modalRectH = 545;
|
||||
|
||||
static var path = '/';
|
||||
function renderFiles(g:kha.graphics2.Graphics) {
|
||||
var appw = kha.System.windowWidth();
|
||||
var apph = kha.System.windowHeight();
|
||||
var left = appw / 2 - modalW / 2;
|
||||
var top = apph / 2 - modalH / 2;
|
||||
|
||||
g.begin(false);
|
||||
g.color = 0xff202020;
|
||||
g.fillRect(left, top, modalW, modalH);
|
||||
g.end();
|
||||
|
||||
var leftRect = Std.int(appw / 2 - modalRectW / 2);
|
||||
var rightRect = Std.int(appw / 2 + modalRectW / 2);
|
||||
var topRect = Std.int(apph / 2 - modalRectH / 2);
|
||||
var bottomRect = Std.int(apph / 2 + modalRectH / 2);
|
||||
topRect += modalHeaderH;
|
||||
|
||||
uimodal.begin(g);
|
||||
if (uimodal.window(Id.handle(), leftRect, topRect, modalRectW, modalRectH - 100)) {
|
||||
var pathHandle = Id.handle();
|
||||
pathHandle.text = uimodal.textInput(pathHandle);
|
||||
path = uimodal.fileBrowser(pathHandle, foldersOnly);
|
||||
}
|
||||
uimodal.end(false);
|
||||
|
||||
g.begin(false);
|
||||
|
||||
uimodal.beginRegion(g, rightRect - 100, bottomRect - 30, 100);
|
||||
if (uimodal.button("OK")) {
|
||||
showFiles = false;
|
||||
filesDone(path);
|
||||
}
|
||||
uimodal.endRegion(false);
|
||||
|
||||
uimodal.beginRegion(g, rightRect - 200, bottomRect - 30, 100);
|
||||
if (uimodal.button("Cancel")) {
|
||||
showFiles = false;
|
||||
}
|
||||
uimodal.endRegion();
|
||||
|
||||
g.end();
|
||||
}
|
||||
|
||||
inline function scaled(f: Float): Int { return Std.int(f * cui.SCALE()); }
|
||||
}
|
399
lib/leenkx2d/Sources/lnx2d/ElementController.hx
Normal file
399
lib/leenkx2d/Sources/lnx2d/ElementController.hx
Normal file
@ -0,0 +1,399 @@
|
||||
package lnx2d;
|
||||
|
||||
// Kha
|
||||
import kha.math.Vector2;
|
||||
import kha.input.KeyCode;
|
||||
import kha.graphics2.Graphics;
|
||||
using kha.graphics2.GraphicsExtension;
|
||||
|
||||
// Zui
|
||||
import zui.Zui;
|
||||
import leenkx.ui.Canvas;
|
||||
|
||||
// Editor
|
||||
import lnx2d.tools.Math;
|
||||
import lnx2d.ui.UIProperties;
|
||||
import lnx2d.tools.CanvasTools;
|
||||
|
||||
class ElementController {
|
||||
|
||||
static var ui:Zui;
|
||||
static var cui:Zui;
|
||||
|
||||
public static var isManipulating = false;
|
||||
static var transformInitInput:Vector2;
|
||||
static var transformInitPos:Vector2;
|
||||
static var transformInitRot:Float;
|
||||
static var transformInitSize:Vector2;
|
||||
// Was the transformation editing started by dragging the mouse
|
||||
static var transformStartedMouse = false;
|
||||
static var drag = false;
|
||||
static var dragLeft = false;
|
||||
static var dragTop = false;
|
||||
static var dragRight = false;
|
||||
static var dragBottom = false;
|
||||
static var grab = false;
|
||||
static var grabX = false;
|
||||
static var grabY = false;
|
||||
static var rotate = false;
|
||||
static var newElementSelected = false;
|
||||
|
||||
public static var handleSize(get, null):Int;
|
||||
static inline function get_handleSize():Int { return Std.int(8 * ui.SCALE()); }
|
||||
|
||||
public static function initialize(ui: Zui, cui: Zui) {
|
||||
ElementController.ui = ui;
|
||||
ElementController.cui = cui;
|
||||
}
|
||||
|
||||
public static function selectElement(canvas:TCanvas) {
|
||||
if (ui == null) return;
|
||||
|
||||
var selectButton = Main.prefs.keyMap.selectMouseButton;
|
||||
if (selectButton == "Left" && ui.inputStarted && ui.inputDown ||
|
||||
selectButton == "Right" && ui.inputStartedR && ui.inputDownR) {
|
||||
|
||||
// Deselect
|
||||
var lastSelected = Editor.selectedElem;
|
||||
Editor.selectedElem = null;
|
||||
|
||||
newElementSelected = false;
|
||||
|
||||
// Elements are sorted by z position (descending), so the topmost element will get
|
||||
// selected if multiple elements overlap each other at the mouse position
|
||||
var sorted_elements = canvas.elements.copy();
|
||||
sorted_elements.reverse();
|
||||
for (elem in sorted_elements) {
|
||||
var anchorOffset = Canvas.getAnchorOffset(canvas, elem);
|
||||
var ex = scaled(Math.absx(canvas, elem)) + anchorOffset[0];
|
||||
var ey = scaled(Math.absy(canvas, elem)) + anchorOffset[1];
|
||||
var ew = scaled(elem.width);
|
||||
var eh = scaled(elem.height);
|
||||
// Element center
|
||||
var cx = canvas.x + ex + ew / 2;
|
||||
var cy = canvas.y + ey + eh / 2;
|
||||
|
||||
var rotHandleX = cx - handleSize / 2;
|
||||
var rotHandleY = canvas.y + ey - handleSize * 2 - handleSize / 2;
|
||||
var rotHandleH = handleSize * 2 + handleSize / 2;
|
||||
|
||||
if (Math.hitbox(cui, canvas.x + ex - handleSize / 2, canvas.y + ey - handleSize / 2, ew + handleSize, eh + handleSize, elem.rotation)
|
||||
|| (Math.hitbox(cui, rotHandleX, rotHandleY, handleSize, rotHandleH, elem.rotation, [cx, cy]) // Rotation handle hitbox
|
||||
&& lastSelected == elem)) { // Don't select elements other than the currently selected by their rotation handle
|
||||
Editor.selectedElem = elem;
|
||||
|
||||
if (lastSelected != elem)
|
||||
newElementSelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// force properties redraw to show selection
|
||||
UIProperties.hwin.redraws = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public static function render(g:Graphics, canvas:TCanvas) {
|
||||
|
||||
// Outline selected elem
|
||||
if (Editor.selectedElem != null) {
|
||||
var anchorOffset = Canvas.getAnchorOffset(canvas, Editor.selectedElem);
|
||||
|
||||
// Resize rects
|
||||
var ex = scaled(Math.absx(canvas, Editor.selectedElem)) + anchorOffset[0];
|
||||
var ey = scaled(Math.absy(canvas, Editor.selectedElem)) + anchorOffset[1];
|
||||
var ew = scaled(Editor.selectedElem.width);
|
||||
var eh = scaled(Editor.selectedElem.height);
|
||||
// Element center
|
||||
var cx = canvas.x + ex + ew / 2;
|
||||
var cy = canvas.y + ey + eh / 2;
|
||||
g.pushRotation(Editor.selectedElem.rotation, cx, cy);
|
||||
|
||||
// Draw element outline
|
||||
g.color = 0xffffffff;
|
||||
g.drawRect(canvas.x + ex, canvas.y + ey, ew, eh);
|
||||
g.color = 0xff000000;
|
||||
g.drawRect(canvas.x + ex + 1, canvas.y + ey + 1, ew, eh);
|
||||
g.color = 0xffffffff;
|
||||
|
||||
// Rotate mouse coords in opposite direction as the element
|
||||
var rotatedInput:Vector2 = Math.rotatePoint(ui.inputX, ui.inputY, cx, cy, -Editor.selectedElem.rotation);
|
||||
|
||||
// Draw corner drag handles
|
||||
for (handlePosX in 0...3) {
|
||||
// 0 = Left, 0.5 = Center, 1 = Right
|
||||
var handlePosX:Float = handlePosX / 2;
|
||||
|
||||
for (handlePosY in 0...3) {
|
||||
// 0 = Top, 0.5 = Center, 1 = Bottom
|
||||
var handlePosY:Float = handlePosY / 2;
|
||||
|
||||
if (handlePosX == 0.5 && handlePosY == 0.5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var hX = canvas.x + ex + ew * handlePosX - handleSize / 2;
|
||||
var hY = canvas.y + ey + eh * handlePosY - handleSize / 2;
|
||||
|
||||
// Check if the handle is currently dragged (not necessarily hovered!)
|
||||
var dragged = false;
|
||||
|
||||
if (handlePosX == 0 && dragLeft) {
|
||||
if (handlePosY == 0 && dragTop) dragged = true;
|
||||
else if (handlePosY == 0.5 && !(dragTop || dragBottom)) dragged = true;
|
||||
else if (handlePosY == 1 && dragBottom) dragged = true;
|
||||
} else if (handlePosX == 0.5 && !(dragLeft || dragRight)) {
|
||||
if (handlePosY == 0 && dragTop) dragged = true;
|
||||
else if (handlePosY == 1 && dragBottom) dragged = true;
|
||||
} else if (handlePosX == 1 && dragRight) {
|
||||
if (handlePosY == 0 && dragTop) dragged = true;
|
||||
else if (handlePosY == 0.5 && !(dragTop || dragBottom)) dragged = true;
|
||||
else if (handlePosY == 1 && dragBottom) dragged = true;
|
||||
}
|
||||
dragged = dragged && drag;
|
||||
|
||||
|
||||
// Hover
|
||||
if (rotatedInput.x > hX && rotatedInput.x < hX + handleSize || dragged) {
|
||||
if (rotatedInput.y > hY && rotatedInput.y < hY + handleSize || dragged) {
|
||||
g.color = 0xff205d9c;
|
||||
g.fillRect(hX, hY, handleSize, handleSize);
|
||||
g.color = 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
g.drawRect(hX, hY, handleSize, handleSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw rotation handle
|
||||
g.drawLine(cx, canvas.y + ey, cx, canvas.y + ey - handleSize * 2);
|
||||
|
||||
var rotHandleCenter = new Vector2(cx, canvas.y + ey - handleSize * 2);
|
||||
if (rotatedInput.sub(rotHandleCenter).length <= handleSize / 2 || rotate) {
|
||||
g.color = 0xff205d9c;
|
||||
g.fillCircle(rotHandleCenter.x, rotHandleCenter.y, handleSize / 2);
|
||||
g.color = 0xffffffff;
|
||||
}
|
||||
g.drawCircle(rotHandleCenter.x, rotHandleCenter.y, handleSize / 2);
|
||||
|
||||
g.popTransformation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function update(ui:Zui, cui:Zui, canvas:TCanvas) {
|
||||
lnx2d.ElementController.ui = ui;
|
||||
lnx2d.ElementController.cui = cui;
|
||||
|
||||
if (newElementSelected)
|
||||
return;
|
||||
|
||||
if (Editor.selectedElem != null) {
|
||||
var elem = Editor.selectedElem;
|
||||
var anchorOffset = Canvas.getAnchorOffset(canvas, elem);
|
||||
|
||||
var ex = scaled(Math.absx(canvas, elem)) + anchorOffset[0];
|
||||
var ey = scaled(Math.absy(canvas, elem)) + anchorOffset[1];
|
||||
var ew = scaled(elem.width);
|
||||
var eh = scaled(elem.height);
|
||||
var rotatedInput:Vector2 = Math.rotatePoint(ui.inputX, ui.inputY, canvas.x + ex + ew / 2, canvas.y + ey + eh / 2, -elem.rotation);
|
||||
|
||||
if (ui.inputStarted && ui.inputDown) {
|
||||
// Drag selected element
|
||||
if (Math.hitbox(ui, canvas.x + ex - handleSize / 2, canvas.y + ey - handleSize / 2, ew + handleSize, eh + handleSize, Editor.selectedElem.rotation)) {
|
||||
drag = true;
|
||||
// Resize
|
||||
dragLeft = dragRight = dragTop = dragBottom = false;
|
||||
if (rotatedInput.x > canvas.x + ex + ew - handleSize) dragRight = true;
|
||||
else if (rotatedInput.x < canvas.x + ex + handleSize) dragLeft = true;
|
||||
if (rotatedInput.y > canvas.y + ey + eh - handleSize) dragBottom = true;
|
||||
else if (rotatedInput.y < canvas.y + ey + handleSize) dragTop = true;
|
||||
|
||||
startElementManipulation(true);
|
||||
|
||||
} else {
|
||||
var rotHandleCenter = new Vector2(canvas.x + ex + ew / 2, canvas.y + ey - handleSize * 2);
|
||||
var inputPos = rotatedInput.sub(rotHandleCenter);
|
||||
|
||||
// Rotate selected element
|
||||
if (inputPos.length <= handleSize) {
|
||||
rotate = true;
|
||||
startElementManipulation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isManipulating) {
|
||||
UIProperties.hwin.redraws = 2;
|
||||
|
||||
// Confirm
|
||||
if ((transformStartedMouse && ui.inputReleased) || (!transformStartedMouse && ui.inputStarted)) {
|
||||
endElementManipulation();
|
||||
|
||||
// Reset
|
||||
} else if ((ui.isKeyPressed && ui.isEscapeDown) || ui.inputStartedR) {
|
||||
endElementManipulation(true);
|
||||
|
||||
} else if (drag) {
|
||||
var transformDelta = new Vector2(ui.inputX, ui.inputY).sub(transformInitInput);
|
||||
|
||||
if (!transformStartedMouse) {
|
||||
if (ui.isKeyPressed && ui.key == KeyCode.X) {
|
||||
elem.width = Std.int(transformInitSize.x);
|
||||
elem.height = Std.int(transformInitSize.y);
|
||||
dragRight = true;
|
||||
dragBottom = !dragBottom;
|
||||
}
|
||||
if (ui.isKeyPressed && ui.key == KeyCode.Y) {
|
||||
elem.width = Std.int(transformInitSize.x);
|
||||
elem.height = Std.int(transformInitSize.y);
|
||||
dragBottom = true;
|
||||
dragRight = !dragRight;
|
||||
}
|
||||
}
|
||||
|
||||
if (dragRight) {
|
||||
transformDelta.x = Math.calculateTransformDelta(ui, Editor.gridSnapPos, Editor.gridUseRelative, Editor.gridSize, transformDelta.x, transformInitPos.x + transformInitSize.x);
|
||||
elem.width = Std.int(transformInitSize.x + transformDelta.x);
|
||||
} else if (dragLeft) {
|
||||
transformDelta.x = Math.calculateTransformDelta(ui, Editor.gridSnapPos, Editor.gridUseRelative, Editor.gridSize, transformDelta.x, transformInitPos.x);
|
||||
elem.x = transformInitPos.x + transformDelta.x;
|
||||
elem.width = Std.int(transformInitSize.x - transformDelta.x);
|
||||
}
|
||||
if (dragBottom) {
|
||||
transformDelta.y = Math.calculateTransformDelta(ui, Editor.gridSnapPos, Editor.gridUseRelative, Editor.gridSize, transformDelta.y, transformInitPos.y + transformInitSize.y);
|
||||
elem.height = Std.int(transformInitSize.y + transformDelta.y);
|
||||
}
|
||||
else if (dragTop) {
|
||||
transformDelta.y = Math.calculateTransformDelta(ui, Editor.gridSnapPos, Editor.gridUseRelative, Editor.gridSize, transformDelta.y, transformInitPos.y);
|
||||
elem.y = transformInitPos.y + transformDelta.y;
|
||||
elem.height = Std.int(transformInitSize.y - transformDelta.y);
|
||||
}
|
||||
|
||||
if (elem.type != ElementType.Image) {
|
||||
if (elem.width < 1) elem.width = 1;
|
||||
if (elem.height < 1) elem.height = 1;
|
||||
}
|
||||
|
||||
if (!dragLeft && !dragRight && !dragBottom && !dragTop) {
|
||||
grab = true;
|
||||
grabX = true;
|
||||
grabY = true;
|
||||
drag = false;
|
||||
} else {
|
||||
// Ensure there the delta is 0 on unused axes
|
||||
if (!dragBottom && !dragTop) transformDelta.y = 0;
|
||||
else if (!dragLeft && !dragRight) transformDelta.y = 0;
|
||||
|
||||
Editor.currentOperation = ' x: ${elem.x} y: ${elem.y} w: ${elem.width} h: ${elem.height} (dx: ${transformDelta.x} dy: ${transformDelta.y})';
|
||||
}
|
||||
|
||||
} else if (grab) {
|
||||
var transformDelta = new Vector2(ui.inputX, ui.inputY).sub(transformInitInput);
|
||||
|
||||
if (ui.isKeyPressed && ui.key == KeyCode.X) {
|
||||
elem.x = transformInitPos.x;
|
||||
elem.y = transformInitPos.y;
|
||||
grabX = true;
|
||||
grabY = !grabY;
|
||||
}
|
||||
if (ui.isKeyPressed && ui.key == KeyCode.Y) {
|
||||
elem.x = transformInitPos.x;
|
||||
elem.y = transformInitPos.y;
|
||||
grabY = true;
|
||||
grabX = !grabX;
|
||||
}
|
||||
|
||||
if (grabX) {
|
||||
transformDelta.x = Math.calculateTransformDelta(ui, Editor.gridSnapPos, Editor.gridUseRelative, Editor.gridSize, transformDelta.x, transformInitPos.x);
|
||||
elem.x = Std.int(transformInitPos.x + transformDelta.x);
|
||||
}
|
||||
if (grabY) {
|
||||
transformDelta.y = Math.calculateTransformDelta(ui, Editor.gridSnapPos, Editor.gridUseRelative, Editor.gridSize, transformDelta.y, transformInitPos.y);
|
||||
elem.y = Std.int(transformInitPos.y + transformDelta.y);
|
||||
}
|
||||
|
||||
// Ensure there the delta is 0 on unused axes
|
||||
if (!grabX) transformDelta.x = 0;
|
||||
else if (!grabY) transformDelta.y = 0;
|
||||
|
||||
Editor.currentOperation = ' x: ${elem.x} y: ${elem.y} (dx: ${transformDelta.x} dy: ${transformDelta.y})';
|
||||
|
||||
} else if (rotate) {
|
||||
var elemCenter = new Vector2(canvas.x + ex + ew / 2, canvas.y + ey + eh / 2);
|
||||
var inputPos = new Vector2(ui.inputX, ui.inputY).sub(elemCenter);
|
||||
|
||||
// inputPos.x and inputPos.y are both positive when the mouse is in the lower right
|
||||
// corner of the elements center, so the positive x axis used for the angle calculation
|
||||
// in atan2() is equal to the global negative y axis. That's why we have to invert the
|
||||
// angle and add Pi to get the correct rotation. atan2() also returns an angle in the
|
||||
// intervall (-PI, PI], so we don't have to calculate the angle % PI*2 anymore.
|
||||
var inputAngle = -std.Math.atan2(inputPos.x, inputPos.y) + std.Math.PI;
|
||||
|
||||
// Ctrl toggles rotation step mode
|
||||
if ((ui.isKeyDown && ui.key == Main.prefs.keyMap.gridInvert) != Editor.useRotationSteps) {
|
||||
inputAngle = std.Math.round(inputAngle / Editor.rotationSteps) * Editor.rotationSteps;
|
||||
}
|
||||
|
||||
elem.rotation = inputAngle;
|
||||
Editor.currentOperation = " Rot: " + Math.roundPrecision(Math.toDegrees(inputAngle), 2) + "deg";
|
||||
}
|
||||
}
|
||||
|
||||
if (ui.isKeyPressed && !ui.isTyping) {
|
||||
if (!grab && ui.key == Main.prefs.keyMap.grabKey){startElementManipulation(); grab = true; grabX = true; grabY = true;}
|
||||
if (!drag && ui.key == Main.prefs.keyMap.sizeKey) {startElementManipulation(); drag = true; dragLeft = false; dragTop = false; dragRight = true; dragBottom = true;}
|
||||
if (!rotate && ui.key == Main.prefs.keyMap.rotateKey) {startElementManipulation(); rotate = true;}
|
||||
|
||||
if (!isManipulating) {
|
||||
// Move with arrows
|
||||
if (ui.key == KeyCode.Left) Editor.gridSnapPos ? elem.x -= Editor.gridSize : elem.x--;
|
||||
if (ui.key == KeyCode.Right) Editor.gridSnapPos ? elem.x += Editor.gridSize : elem.x++;
|
||||
if (ui.key == KeyCode.Up) Editor.gridSnapPos ? elem.y -= Editor.gridSize : elem.y--;
|
||||
if (ui.key == KeyCode.Down) Editor.gridSnapPos ? elem.y += Editor.gridSize : elem.y++;
|
||||
|
||||
if (ui.isBackspaceDown || ui.isDeleteDown){
|
||||
CanvasTools.removeElem(canvas, Editor.selectedElem);
|
||||
Editor.selectedElem = null;
|
||||
}
|
||||
else if (ui.key == KeyCode.D) Editor.selectedElem = CanvasTools.duplicateElem(canvas, elem);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
endElementManipulation();
|
||||
}
|
||||
}
|
||||
|
||||
static function startElementManipulation(?mousePressed=false) {
|
||||
if (isManipulating) endElementManipulation(true);
|
||||
|
||||
transformInitInput = new Vector2(ui.inputX, ui.inputY);
|
||||
transformInitPos = new Vector2(Editor.selectedElem.x, Editor.selectedElem.y);
|
||||
transformInitSize = new Vector2(Editor.selectedElem.width, Editor.selectedElem.height);
|
||||
transformInitRot = Editor.selectedElem.rotation;
|
||||
transformStartedMouse = mousePressed;
|
||||
|
||||
isManipulating = true;
|
||||
}
|
||||
|
||||
static function endElementManipulation(reset=false) {
|
||||
if (reset) {
|
||||
Editor.selectedElem.x = transformInitPos.x;
|
||||
Editor.selectedElem.y = transformInitPos.y;
|
||||
Editor.selectedElem.width = Std.int(transformInitSize.x);
|
||||
Editor.selectedElem.height = Std.int(transformInitSize.y);
|
||||
Editor.selectedElem.rotation = transformInitRot;
|
||||
}
|
||||
|
||||
isManipulating = false;
|
||||
|
||||
grab = false;
|
||||
drag = false;
|
||||
rotate = false;
|
||||
|
||||
transformStartedMouse = false;
|
||||
Editor.currentOperation = "";
|
||||
}
|
||||
|
||||
static inline function scaled(f: Float): Int { return Std.int(f * cui.SCALE()); }
|
||||
}
|
26
lib/leenkx2d/Sources/lnx2d/Path.hx
Normal file
26
lib/leenkx2d/Sources/lnx2d/Path.hx
Normal file
@ -0,0 +1,26 @@
|
||||
package lnx2d;
|
||||
|
||||
class Path {
|
||||
|
||||
public static function toRelative(path:String, cwd:String):String {
|
||||
path = haxe.io.Path.normalize(path);
|
||||
cwd = haxe.io.Path.normalize(cwd);
|
||||
|
||||
var ar:Array<String> = [];
|
||||
var ar1 = path.split("/");
|
||||
var ar2 = cwd.split("/");
|
||||
|
||||
var index = 0;
|
||||
while (ar1[index] == ar2[index]) index++;
|
||||
|
||||
for (i in 0...ar2.length - index) ar.push("..");
|
||||
|
||||
for (i in index...ar1.length) ar.push(ar1[i]);
|
||||
|
||||
return ar.join("/");
|
||||
}
|
||||
|
||||
public static function toAbsolute(path:String, cwd:String):String {
|
||||
return haxe.io.Path.normalize(cwd + "/" + path);
|
||||
}
|
||||
}
|
229
lib/leenkx2d/Sources/lnx2d/tools/CanvasTools.hx
Normal file
229
lib/leenkx2d/Sources/lnx2d/tools/CanvasTools.hx
Normal file
@ -0,0 +1,229 @@
|
||||
package lnx2d.tools;
|
||||
|
||||
// Zui
|
||||
import zui.Zui;
|
||||
import leenkx.ui.Canvas;
|
||||
|
||||
class CanvasTools {
|
||||
|
||||
public static function makeElem(cui:Zui, canvas:TCanvas, type:ElementType) {
|
||||
var name = "";
|
||||
var height = cui.t.ELEMENT_H;
|
||||
var alignment = Align.Left;
|
||||
|
||||
switch (type) {
|
||||
case ElementType.Text:
|
||||
name = unique("Text", canvas.elements, "name");
|
||||
case ElementType.Button:
|
||||
name = unique("Button", canvas.elements, "name");
|
||||
alignment = Align.Center;
|
||||
case ElementType.Image:
|
||||
name = unique("Image", canvas.elements, "name");
|
||||
height = 100;
|
||||
case ElementType.FRectangle:
|
||||
name = unique("Filled_Rectangle", canvas.elements, "name");
|
||||
height = 100;
|
||||
case ElementType.FCircle:
|
||||
name = unique("Filled_Circle", canvas.elements, "name");
|
||||
case ElementType.Rectangle:
|
||||
name = unique("Rectangle", canvas.elements, "name");
|
||||
height = 100;
|
||||
case ElementType.FTriangle:
|
||||
name = unique("Filled_Triangle", canvas.elements, "name");
|
||||
case ElementType.Triangle:
|
||||
name = unique("Triangle", canvas.elements, "name");
|
||||
case ElementType.Circle:
|
||||
name = unique("Circle", canvas.elements, "name");
|
||||
case ElementType.Check:
|
||||
name = unique("Check", canvas.elements, "name");
|
||||
case ElementType.Radio:
|
||||
name = unique("Radio", canvas.elements, "name");
|
||||
case ElementType.Combo:
|
||||
name = unique("Combo", canvas.elements, "name");
|
||||
case ElementType.Slider:
|
||||
name = unique("Slider", canvas.elements, "name");
|
||||
alignment = Align.Right;
|
||||
case ElementType.TextInput:
|
||||
name = unique("TextInput", canvas.elements, "name");
|
||||
case ElementType.KeyInput:
|
||||
name = unique("KeyInput", canvas.elements, "name");
|
||||
case ElementType.TextArea:
|
||||
name = unique("TextArea", canvas.elements, "name");
|
||||
case ElementType.ProgressBar:
|
||||
name = unique("Progress_bar", canvas.elements, "name");
|
||||
case ElementType.CProgressBar:
|
||||
name = unique("CProgress_bar", canvas.elements, "name");
|
||||
case ElementType.Empty:
|
||||
name = unique("Empty", canvas.elements, "name");
|
||||
height = 100;
|
||||
}
|
||||
var elem:TElement = {
|
||||
id: Canvas.getElementId(canvas),
|
||||
type: type,
|
||||
name: name,
|
||||
event: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 150,
|
||||
height: height,
|
||||
rotation: 0,
|
||||
text: "My " + name,
|
||||
asset: "",
|
||||
progress_at: 0,
|
||||
progress_total: 100,
|
||||
strength: 1,
|
||||
alignment: cast(alignment, Int),
|
||||
anchor: 0,
|
||||
parent: null,
|
||||
children: [],
|
||||
visible: true
|
||||
};
|
||||
canvas.elements.push(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique string for a given array based on the string s.
|
||||
*
|
||||
* @param s The string that is returned in a unique form
|
||||
* @param data The array where the string should be unique
|
||||
* @param elemAttr The name of the attribute of the data elements to be compared with the string.
|
||||
* @param counter=-1 Internal use only, do not overwrite!
|
||||
* @return String A unique string in the given array
|
||||
*/
|
||||
//TODO: Here
|
||||
public static function unique(s:String, data:Array<Dynamic>, elemAttr:String, counter=-1): String {
|
||||
var originalName = s;
|
||||
|
||||
// Reconstruct the original name
|
||||
var split = s.lastIndexOf(".");
|
||||
if (split != -1) {
|
||||
// .001, .002...
|
||||
var suffix = s.substring(split);
|
||||
if (suffix.length == 4) {
|
||||
originalName = s.substring(0, split);
|
||||
}
|
||||
}
|
||||
|
||||
for (elem in data) {
|
||||
if (Reflect.getProperty(elem, elemAttr) == s) {
|
||||
if (counter > -1) {
|
||||
counter++;
|
||||
var counterLen = Std.string(counter).length;
|
||||
if (counterLen > 3) counterLen = 3;
|
||||
var padding = ".";
|
||||
for (i in 0...3 - counterLen) {
|
||||
padding += "0";
|
||||
}
|
||||
|
||||
return unique(originalName + padding + Std.string(counter), data, elemAttr, counter);
|
||||
|
||||
} else {
|
||||
return unique(originalName, data, elemAttr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static function moveElem(canvas:TCanvas, elem:TElement, d:Int) {
|
||||
var ar = canvas.elements;
|
||||
var i = ar.indexOf(elem);
|
||||
var p = elem.parent;
|
||||
|
||||
while (true) {
|
||||
i += d;
|
||||
if (i < 0 || i >= ar.length) break;
|
||||
|
||||
if (ar[i].parent == p) {
|
||||
ar.remove(elem);
|
||||
ar.insert(i, elem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeElem(canvas:TCanvas, elem:TElement) {
|
||||
if (elem.children != null) for (id in elem.children) removeElem(canvas, CanvasTools.elemById(canvas, id));
|
||||
canvas.elements.remove(elem);
|
||||
if (elem.parent != null) {
|
||||
CanvasTools.elemById(canvas, elem.parent).children.remove(elem.id);
|
||||
elem.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function elemById(canvas: TCanvas, id: Int): TElement {
|
||||
for (e in canvas.elements) if (e.id == id) return e;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function unparent(canvas:TCanvas, elem:TElement) {
|
||||
var parent = CanvasTools.elemById(canvas, elem.parent);
|
||||
if (parent != null) {
|
||||
elem.x += Math.absx(canvas, parent);
|
||||
elem.y += Math.absy(canvas, parent);
|
||||
elem.parent = null;
|
||||
parent.children.remove(elem.id);
|
||||
}
|
||||
}
|
||||
|
||||
public static function setParent(canvas:TCanvas, elem:TElement, parent:TElement) {
|
||||
var oldParent = CanvasTools.elemById(canvas, elem.parent);
|
||||
if (oldParent == parent) return;
|
||||
unparent(canvas, elem); //Unparent first if we already have a parent
|
||||
|
||||
if (parent != null) { //Parent
|
||||
if (parent.children == null) elem.children = [];
|
||||
parent.children.push(elem.id);
|
||||
elem.parent = parent.id;
|
||||
elem.x -= Math.absx(canvas, parent);
|
||||
elem.y -= Math.absy(canvas, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public static function duplicateElem(canvas:TCanvas, elem:TElement, parentId:Null<Int> = null):TElement {
|
||||
if (elem != null) {
|
||||
if (parentId == null) parentId = elem.parent;
|
||||
var dupe:TElement = {
|
||||
id: Canvas.getElementId(canvas),
|
||||
type: elem.type,
|
||||
name: elem.name,
|
||||
event: elem.event,
|
||||
x: elem.x + 10,
|
||||
y: elem.y + 10,
|
||||
width: elem.width,
|
||||
height: elem.height,
|
||||
rotation: elem.rotation,
|
||||
text: elem.text,
|
||||
asset: elem.asset,
|
||||
color: elem.color,
|
||||
color_text: elem.color_text,
|
||||
color_hover: elem.color_hover,
|
||||
color_press: elem.color_press,
|
||||
color_progress: elem.color_progress,
|
||||
progress_at: elem.progress_at,
|
||||
progress_total: elem.progress_total,
|
||||
strength: elem.strength,
|
||||
anchor: elem.anchor,
|
||||
parent: parentId,
|
||||
children: [],
|
||||
visible: elem.visible
|
||||
};
|
||||
canvas.elements.push(dupe);
|
||||
if (parentId != null) {
|
||||
var parentElem = CanvasTools.elemById(canvas, parentId);
|
||||
parentElem.children.push(dupe.id);
|
||||
if (elem.parent != parentId) {
|
||||
dupe.x = elem.x;
|
||||
dupe.y = elem.y;
|
||||
}
|
||||
}
|
||||
for(child in elem.children) {
|
||||
duplicateElem(canvas, CanvasTools.elemById(canvas, child), dupe.id);
|
||||
}
|
||||
|
||||
return dupe;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
94
lib/leenkx2d/Sources/lnx2d/tools/Math.hx
Normal file
94
lib/leenkx2d/Sources/lnx2d/tools/Math.hx
Normal file
@ -0,0 +1,94 @@
|
||||
package lnx2d.tools;
|
||||
|
||||
// Kha
|
||||
import kha.math.Vector2;
|
||||
|
||||
// Zui
|
||||
import zui.Zui;
|
||||
import leenkx.ui.Canvas.TCanvas;
|
||||
import leenkx.ui.Canvas.TElement;
|
||||
|
||||
class Math {
|
||||
|
||||
public static inline function toDegrees(radians:Float):Float { return radians * 57.29578; }
|
||||
public static inline function toRadians(degrees:Float):Float { return degrees * 0.0174532924; }
|
||||
|
||||
/**
|
||||
* Calculates if the mouse is in the given hitbox rectangle.
|
||||
*
|
||||
* @param ui The Zui object of which this function should take the mouse position
|
||||
* @param x The x position of the hitbox
|
||||
* @param y The y position of the hitbox
|
||||
* @param w The width of the hitbox
|
||||
* @param h The height of the hitbox
|
||||
* @param rotation The rotation of the hitbox in radians, default = 0.0
|
||||
* @param center (Optional) The [x, y] coordinates of the center of rotation. If not given or null, use the center of the rectangle given by (x, y, w, h)
|
||||
* @return Bool
|
||||
*/
|
||||
public static function hitbox(ui: Zui, x: Float, y: Float, w: Float, h: Float, rotation: Float = 0.0, ?center: Array<Float>):Bool {
|
||||
if (center != null && center.length != 2) {
|
||||
throw "lnx2d.tools.Math.hitbox(): 'center' argument must consist of two values!";
|
||||
}
|
||||
|
||||
if (center == null) center = [x + w / 2, y + h / 2];
|
||||
|
||||
var rotatedInput:Vector2 = rotatePoint(ui.inputX, ui.inputY, center[0], center[1], -rotation);
|
||||
return rotatedInput.x > x && rotatedInput.x < x + w && rotatedInput.y > y && rotatedInput.y < y + h;
|
||||
}
|
||||
|
||||
public static function absx(canvas:TCanvas, e:TElement):Float {
|
||||
if (e == null) return 0;
|
||||
return e.x + absx(canvas, CanvasTools.elemById(canvas, e.parent));
|
||||
}
|
||||
|
||||
public static function absy(canvas:TCanvas, e:TElement):Float {
|
||||
if (e == null) return 0;
|
||||
return e.y + absy(canvas, CanvasTools.elemById(canvas, e.parent));
|
||||
}
|
||||
|
||||
public static function roundPrecision(v:Float, ?precision=0):Float {
|
||||
v *= std.Math.pow(10, precision);
|
||||
|
||||
v = Std.int(v) * 1.0;
|
||||
v /= std.Math.pow(10, precision);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function rotatePoint(pointX: Float, pointY: Float, centerX: Float, centerY: Float, angle:Float): Vector2 {
|
||||
pointX -= centerX;
|
||||
pointY -= centerY;
|
||||
|
||||
var x = pointX * std.Math.cos(angle) - pointY * std.Math.sin(angle);
|
||||
var y = pointX * std.Math.sin(angle) + pointY * std.Math.cos(angle);
|
||||
|
||||
return new Vector2(centerX + x, centerY + y);
|
||||
}
|
||||
|
||||
public static function calculateTransformDelta(ui:Zui, gSP:Bool, gUR:Bool, gS:Int, value:Float, ?offset=0.0):Float {
|
||||
var precisionMode = ui.isKeyDown && ui.key == Main.prefs.keyMap.slowMovement;
|
||||
var enabled = gSP != (ui.isKeyDown && (ui.key == Main.prefs.keyMap.gridInvert));
|
||||
var useOffset = gUR != (ui.isKeyDown && (ui.key == Main.prefs.keyMap.gridInvertRelative));
|
||||
|
||||
if (!enabled) return precisionMode ? value / 2 : value;
|
||||
|
||||
// Round the delta value to steps of gridSize
|
||||
value = std.Math.round(value / gS) * gS;
|
||||
|
||||
if (precisionMode) value /= 2;
|
||||
|
||||
// Apply an offset
|
||||
if (useOffset && offset != 0) {
|
||||
offset = offset % gS;
|
||||
|
||||
// Round to nearest grid position instead of rounding off
|
||||
if (offset > gS / 2) {
|
||||
offset = -(gS - offset);
|
||||
}
|
||||
|
||||
value -= offset;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
615
lib/leenkx2d/Sources/lnx2d/ui/UIProperties.hx
Normal file
615
lib/leenkx2d/Sources/lnx2d/ui/UIProperties.hx
Normal file
@ -0,0 +1,615 @@
|
||||
package lnx2d.ui;
|
||||
|
||||
// Kha
|
||||
import kha.input.KeyCode;
|
||||
|
||||
// Zui
|
||||
import zui.Id;
|
||||
import zui.Zui;
|
||||
import leenkx.ui.Popup;
|
||||
import leenkx.ui.Canvas;
|
||||
using zui.Ext;
|
||||
using leenkx.ui.Ext;
|
||||
|
||||
// Editor
|
||||
import lnx2d.tools.Math;
|
||||
import lnx2d.tools.CanvasTools;
|
||||
|
||||
@:access(zui.Zui)
|
||||
class UIProperties {
|
||||
|
||||
public static var hwin = Id.handle();
|
||||
|
||||
public static function renderProperties(ui:Zui, width:Int, canvas:TCanvas) {
|
||||
|
||||
if (ui.window(hwin, kha.System.windowWidth() - width, 0, width, kha.System.windowHeight())) {
|
||||
|
||||
var htab = Id.handle();
|
||||
if (ui.tab(htab, "Project")) {
|
||||
|
||||
var hpath = Id.handle({text:""});
|
||||
ui.textInput(hpath,"Current file");
|
||||
if(hpath.changed){
|
||||
Main.prefs.path = hpath.text;
|
||||
}
|
||||
|
||||
if (ui.button("Save")) {
|
||||
Assets.save(canvas);
|
||||
}
|
||||
|
||||
if(ui.button("Load")){
|
||||
Assets.load(function(c:TCanvas){
|
||||
Main.inst.canvas = c;
|
||||
hwin.redraws = 2;
|
||||
});
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Canvas")) {
|
||||
ui.indent();
|
||||
|
||||
if (ui.button("New")) {
|
||||
canvas.elements = [];
|
||||
Editor.selectedElem = null;
|
||||
}
|
||||
if (ui.isHovered) ui.tooltip("Create new canvas");
|
||||
|
||||
var handleName = Id.handle({text: canvas.name});
|
||||
handleName.text = canvas.name;
|
||||
ui.textInput(handleName, "Name", Right);
|
||||
if (handleName.changed) {
|
||||
// Themes file is called _themes.json, so canvases should not be named like that
|
||||
if (handleName.text == "_themes") {
|
||||
Popup.showMessage(new Zui(ui.ops), "Sorry!", "\"_themes\" is not a valid canvas name as it is reserved!");
|
||||
handleName.text = canvas.name;
|
||||
} else {
|
||||
canvas.name = handleName.text;
|
||||
}
|
||||
}
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
var handlecw = Id.handle({text: canvas.width + ""});
|
||||
var handlech = Id.handle({text: canvas.height + ""});
|
||||
handlecw.text = canvas.width + "";
|
||||
handlech.text = canvas.height + "";
|
||||
var strw = ui.textInput(handlecw, "Width", Right);
|
||||
var strh = ui.textInput(handlech, "Height", Right);
|
||||
canvas.width = Std.parseInt(strw);
|
||||
canvas.height = Std.parseInt(strh);
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Outliner")) {
|
||||
ui.indent();
|
||||
|
||||
function drawList(h:zui.Zui.Handle, elem:TElement) {
|
||||
var b = false;
|
||||
// Highlight
|
||||
if (Editor.selectedElem == elem) {
|
||||
ui.g.color = 0xff205d9c;
|
||||
ui.g.fillRect(ui._x, ui._y, ui._w, ui.t.ELEMENT_H * ui.SCALE());
|
||||
ui.g.color = 0xffffffff;
|
||||
}
|
||||
var started = ui.getStarted();
|
||||
// Select
|
||||
if (started && !ui.inputDownR) Editor.selectedElem = elem;
|
||||
// Parenting
|
||||
if (started && ui.inputDownR) {
|
||||
if (elem == Editor.selectedElem) CanvasTools.unparent(canvas, elem);
|
||||
else CanvasTools.setParent(canvas, Editor.selectedElem, elem);
|
||||
}
|
||||
// Draw
|
||||
if (elem.children != null && elem.children.length > 0) {
|
||||
ui.row([1/13, 12/13]);
|
||||
b = ui.panel(h.nest(elem.id, {selected: true}), "", true, false, false);
|
||||
ui.text(elem.name);
|
||||
}
|
||||
else {
|
||||
ui._x += 18; // Sign offset
|
||||
ui.text(elem.name);
|
||||
ui._x -= 18;
|
||||
}
|
||||
// Draw children
|
||||
if (b) {
|
||||
var i = elem.children.length;
|
||||
while(i > 0) {
|
||||
i--; //Iterate backwards to avoid reparenting issues.
|
||||
var id = elem.children[elem.children.length - 1 - i];
|
||||
ui.indent();
|
||||
drawList(h, CanvasTools.elemById(canvas, id));
|
||||
ui.unindent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canvas.elements.length > 0) {
|
||||
for (i in 0...canvas.elements.length) {
|
||||
var elem = canvas.elements[canvas.elements.length - 1 - i];
|
||||
if (elem.parent == null) drawList(Id.handle(), elem);
|
||||
}
|
||||
|
||||
ui.row([1/4, 1/4, 1/4, 1/4]);
|
||||
if (ui.button("Up") && Editor.selectedElem != null) {
|
||||
CanvasTools.moveElem(canvas, Editor.selectedElem, 1);
|
||||
}
|
||||
if (ui.isHovered) ui.tooltip("Move element up");
|
||||
|
||||
if (ui.button("Down") && Editor.selectedElem != null) {
|
||||
CanvasTools.moveElem(canvas, Editor.selectedElem,-1);
|
||||
}
|
||||
if (ui.isHovered) ui.tooltip("Move element down");
|
||||
|
||||
if (ui.button("Remove") && Editor.selectedElem != null) {
|
||||
CanvasTools.removeElem(canvas, Editor.selectedElem);
|
||||
Editor.selectedElem = null;
|
||||
}
|
||||
if (ui.isHovered) ui.tooltip("Delete element");
|
||||
|
||||
if (ui.button("Duplicate") && Editor.selectedElem != null) {
|
||||
Editor.selectedElem = CanvasTools.duplicateElem(canvas, Editor.selectedElem);
|
||||
}
|
||||
if (ui.isHovered) ui.tooltip("Create duplicate of element");
|
||||
}
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (Editor.selectedElem != null) {
|
||||
var elem = Editor.selectedElem;
|
||||
var id = elem.id;
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Properties")) {
|
||||
ui.indent();
|
||||
elem.visible = ui.check(Id.handle().nest(id, {selected: elem.visible == null ? true : elem.visible}), "Visible");
|
||||
elem.name = ui.textInput(Id.handle().nest(id, {text: elem.name}), "Name", Right);
|
||||
elem.text = ui.textInput(Id.handle().nest(id, {text: elem.text}), "Text", Right);
|
||||
ui.row([1/4, 1/4, 1/4, 1/4]);
|
||||
var handlex = Id.handle().nest(id, {text: elem.x + ""});
|
||||
var handley = Id.handle().nest(id, {text: elem.y + ""});
|
||||
// if (drag) {
|
||||
handlex.text = elem.x + "";
|
||||
handley.text = elem.y + "";
|
||||
// }
|
||||
var strx = ui.textInput(handlex, "X", Right);
|
||||
var stry = ui.textInput(handley, "Y", Right);
|
||||
elem.x = Std.parseFloat(strx);
|
||||
elem.y = Std.parseFloat(stry);
|
||||
// ui.row([1/2, 1/2]);
|
||||
var handlew = Id.handle().nest(id, {text: elem.width + ""});
|
||||
var handleh = Id.handle().nest(id, {text: elem.height + ""});
|
||||
// if (drag) {
|
||||
handlew.text = elem.width + "";
|
||||
handleh.text = elem.height + "";
|
||||
// }
|
||||
var strw = ui.textInput(handlew, "W", Right);
|
||||
var strh = ui.textInput(handleh, "H", Right);
|
||||
elem.width = Std.int(Std.parseFloat(strw));
|
||||
elem.height = Std.int(Std.parseFloat(strh));
|
||||
if (elem.type == ElementType.Rectangle || elem.type == ElementType.Circle || elem.type == ElementType.Triangle || elem.type == ElementType.ProgressBar || elem.type == ElementType.CProgressBar){
|
||||
var handles = Id.handle().nest(id, {text: elem.strength+""});
|
||||
var strs = ui.textInput(handles, "Line Strength", Right);
|
||||
elem.strength = Std.int(Std.parseFloat(strs));
|
||||
}
|
||||
if (elem.type == ElementType.ProgressBar || elem.type == ElementType.CProgressBar){
|
||||
var handlep = Id.handle().nest(id, {value: elem.progress_at});
|
||||
var slp = ui.slider(handlep, "Progress", 0.0, elem.progress_total, true, 1);
|
||||
var handlespt = Id.handle().nest(id, {text: elem.progress_total+""});
|
||||
var strpt = ui.textInput(handlespt, "Total Progress", Right);
|
||||
elem.progress_total = Std.int(Std.parseFloat(strpt));
|
||||
elem.progress_at = Std.int(slp);
|
||||
}
|
||||
var handlerot = Id.handle().nest(id, {value: Math.roundPrecision(Math.toDegrees(elem.rotation == null ? 0 : elem.rotation), 2)});
|
||||
handlerot.value = Math.roundPrecision(Math.toDegrees(elem.rotation), 2);
|
||||
// Small fix for radian/degree precision errors
|
||||
if (handlerot.value >= 360) handlerot.value = 0;
|
||||
elem.rotation = Math.toRadians(ui.slider(handlerot, "Rotation", 0.0, 360.0, true));
|
||||
var assetPos = ui.combo(Id.handle().nest(id, {position: Assets.getAssetIndex(canvas, elem.asset)}), Assets.getEnumTexts(), "Asset", true, Right);
|
||||
elem.asset = Assets.getEnumTexts()[assetPos];
|
||||
ui.unindent();
|
||||
}
|
||||
if (ui.panel(Id.handle({selected: false}), "Color")){
|
||||
ui.indent();
|
||||
|
||||
function drawColorSelection(idMult: Int, color:Null<Int>, defaultColor:Int) {
|
||||
ui.row([1/2, 1/2]);
|
||||
|
||||
var handleCol = Id.handle().nest(id).nest(idMult, {color: Canvas.getColor(color, defaultColor)});
|
||||
ui.colorField(handleCol, true);
|
||||
|
||||
if (handleCol.changed) {
|
||||
color = handleCol.color;
|
||||
}
|
||||
|
||||
// Follow theme color
|
||||
if (ui.button("Reset") || color == null) {
|
||||
color = null;
|
||||
handleCol.color = defaultColor;
|
||||
handleCol.changed = false;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
var canvasTheme = Canvas.getTheme(canvas.theme);
|
||||
|
||||
switch(elem.type) {
|
||||
case Text:
|
||||
ui.text("Text:");
|
||||
elem.color_text = drawColorSelection(1, elem.color_text, canvasTheme.TEXT_COL);
|
||||
|
||||
case Button:
|
||||
ui.text("Text:");
|
||||
elem.color_text = drawColorSelection(1, elem.color_text, canvasTheme.BUTTON_TEXT_COL);
|
||||
ui.text("Background:");
|
||||
elem.color = drawColorSelection(2, elem.color, canvasTheme.BUTTON_COL);
|
||||
ui.text("On Hover:");
|
||||
elem.color_hover = drawColorSelection(3, elem.color_hover, canvasTheme.BUTTON_HOVER_COL);
|
||||
ui.text("On Pressed:");
|
||||
elem.color_press = drawColorSelection(4, elem.color_press, canvasTheme.BUTTON_PRESSED_COL);
|
||||
|
||||
case Rectangle, FRectangle, Circle, FCircle, Triangle, FTriangle:
|
||||
ui.text("Color:");
|
||||
elem.color = drawColorSelection(1, elem.color, canvasTheme.BUTTON_COL);
|
||||
|
||||
case ProgressBar, CProgressBar:
|
||||
ui.text("Progress:");
|
||||
elem.color_progress = drawColorSelection(1, elem.color_progress, canvasTheme.TEXT_COL);
|
||||
ui.text("Background:");
|
||||
elem.color = drawColorSelection(2, elem.color, canvasTheme.BUTTON_COL);
|
||||
|
||||
case Check, TextInput, KeyInput, Combo, Slider:
|
||||
ui.text("Text:");
|
||||
elem.color_text = drawColorSelection(1, elem.color_text, canvasTheme.TEXT_COL);
|
||||
ui.text("Background:");
|
||||
elem.color = drawColorSelection(2, elem.color, canvasTheme.BUTTON_COL);
|
||||
ui.text("On Hover:");
|
||||
elem.color_hover = drawColorSelection(3, elem.color_hover, canvasTheme.BUTTON_HOVER_COL);
|
||||
|
||||
default:
|
||||
ui.text("This element type has no color settings!");
|
||||
|
||||
}
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Align")) {
|
||||
ui.indent();
|
||||
|
||||
var alignmentHandle = Id.handle().nest(id, {position: elem.alignment});
|
||||
ui.row([1/3, 1/3, 1/3]);
|
||||
ui.radio(alignmentHandle, 0, "Left");
|
||||
ui.radio(alignmentHandle, 1, "Center");
|
||||
ui.radio(alignmentHandle, 2, "Right");
|
||||
Editor.selectedElem.alignment = alignmentHandle.position;
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Anchor")) {
|
||||
ui.indent();
|
||||
var hanch = Id.handle().nest(id, {position: elem.anchor});
|
||||
ui.row([4/11,3/11,4/11]);
|
||||
ui.radio(hanch, 0, "Top-Left");
|
||||
ui.radio(hanch, 1, "Top");
|
||||
ui.radio(hanch, 2, "Top-Right");
|
||||
ui.row([4/11,3/11,4/11]);
|
||||
ui.radio(hanch, 3, "Left");
|
||||
ui.radio(hanch, 4, "Center");
|
||||
ui.radio(hanch, 5, "Right");
|
||||
ui.row([4/11,3/11,4/11]);
|
||||
ui.radio(hanch, 6, "Bot-Left");
|
||||
ui.radio(hanch, 7, "Bottom");
|
||||
ui.radio(hanch, 8, "Bot-Right");
|
||||
elem.anchor = hanch.position;
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Script")) {
|
||||
ui.indent();
|
||||
elem.event = ui.textInput(Id.handle().nest(id, {text: elem.event}), "Event", Right);
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Timeline")) {
|
||||
// ui.indent();
|
||||
// ui.row([1/2,1/2]);
|
||||
// ui.button("Insert");
|
||||
// ui.button("Remove");
|
||||
// ui.unindent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ui.tab(htab, "Themes")) {
|
||||
// Must be accesible all over this place because when deleting themes,
|
||||
// the color handle's child handle at that theme index must be reset.
|
||||
var handleThemeColor = Id.handle();
|
||||
var handleThemeName = Id.handle();
|
||||
var iconSize = 16;
|
||||
|
||||
function drawList(h:zui.Zui.Handle, theme:zui.Themes.TTheme) {
|
||||
// Highlight
|
||||
if (Editor.selectedTheme == theme) {
|
||||
ui.g.color = 0xff205d9c;
|
||||
ui.g.fillRect(0, ui._y, ui._windowW, ui.t.ELEMENT_H * ui.SCALE());
|
||||
ui.g.color = 0xffffffff;
|
||||
}
|
||||
// Highlight default theme
|
||||
if (theme == Canvas.getTheme(canvas.theme)) {
|
||||
var iconMargin = (ui.t.BUTTON_H - iconSize) / 2;
|
||||
ui.g.drawSubImage(kha.Assets.images.icons, ui._x + iconMargin, ui._y + iconMargin, 0, 0, 16, 16);
|
||||
}
|
||||
|
||||
var started = ui.getStarted();
|
||||
// Select
|
||||
if (started && !ui.inputDownR) Editor.selectedTheme = theme;
|
||||
|
||||
// Draw
|
||||
ui._x += iconSize; // Icon offset
|
||||
ui.text(theme.NAME);
|
||||
ui._x -= iconSize;
|
||||
}
|
||||
|
||||
for (theme in Canvas.themes) drawList(Id.handle(), theme);
|
||||
|
||||
ui.row([1/4, 1/4, 1/4, 1/4]);
|
||||
if (ui.button("Add")) {
|
||||
var newTheme = Reflect.copy(leenkx.ui.Themes.light);
|
||||
newTheme.NAME = CanvasTools.unique("New Theme", Canvas.themes, "NAME");
|
||||
|
||||
Canvas.themes.push(newTheme);
|
||||
Editor.selectedTheme = newTheme;
|
||||
}
|
||||
|
||||
if (Editor.selectedTheme == null) ui.enabled = false;
|
||||
if (ui.button("Copy")) {
|
||||
var newTheme = Reflect.copy(Editor.selectedTheme);
|
||||
newTheme.NAME = CanvasTools.unique(newTheme.NAME, Canvas.themes, "NAME");
|
||||
|
||||
Canvas.themes.push(newTheme);
|
||||
Editor.selectedTheme = newTheme;
|
||||
}
|
||||
ui.enabled = true;
|
||||
|
||||
if (Editor.selectedTheme == null) ui.enabled = false;
|
||||
var hName = handleThemeName.nest(Canvas.themes.indexOf(Editor.selectedTheme));
|
||||
if (ui.button("Rename")) {
|
||||
hName.text = Editor.selectedTheme.NAME;
|
||||
Popup.showCustom(
|
||||
new Zui(ui.ops),
|
||||
function(ui:Zui) {
|
||||
ui.textInput(hName);
|
||||
if (ui.button("OK")) {
|
||||
Popup.show = false;
|
||||
hwin.redraws = 2;
|
||||
}
|
||||
},
|
||||
Std.int(ui.inputX), Std.int(ui.inputY), 200, 60);
|
||||
}
|
||||
if (Editor.selectedTheme != null) {
|
||||
var name = Editor.selectedTheme.NAME;
|
||||
if (hName.changed && Editor.selectedTheme.NAME != hName.text) {
|
||||
name = CanvasTools.unique(hName.text, Canvas.themes, "NAME");
|
||||
if (canvas.theme == Editor.selectedTheme.NAME) {
|
||||
canvas.theme = name;
|
||||
}
|
||||
Editor.selectedTheme.NAME = name;
|
||||
}
|
||||
}
|
||||
ui.enabled = true;
|
||||
|
||||
if (Canvas.themes.length == 1 || Editor.selectedTheme == null) ui.enabled = false;
|
||||
if (ui.button("Delete")) {
|
||||
handleThemeColor.unnest(Canvas.themes.indexOf(Editor.selectedTheme));
|
||||
handleThemeName.unnest(Canvas.themes.indexOf(Editor.selectedTheme));
|
||||
|
||||
Canvas.themes.remove(Editor.selectedTheme);
|
||||
|
||||
// Canvas default theme was deleted
|
||||
if (Canvas.getTheme(canvas.theme) == null) {
|
||||
canvas.theme = Canvas.themes[0].NAME;
|
||||
}
|
||||
Editor.selectedTheme = null;
|
||||
}
|
||||
ui.enabled = true;
|
||||
|
||||
if (Editor.selectedTheme == null) ui.enabled = false;
|
||||
if (ui.button("Apply to Canvas")) canvas.theme = Editor.selectedTheme.NAME;
|
||||
|
||||
ui.enabled = true;
|
||||
|
||||
if (Editor.selectedTheme == null) {
|
||||
ui.text("Please select a Theme!");
|
||||
} else {
|
||||
// A Map would be way better, but its order is not guaranteed.
|
||||
var themeColorOptions:Array<Array<String>> = [
|
||||
["Text", "TEXT_COL"],
|
||||
["Elements", "BUTTON_COL", "BUTTON_TEXT_COL", "BUTTON_HOVER_COL", "BUTTON_PRESSED_COL", "ACCENT_COL", "ACCENT_HOVER_COL", "ACCENT_SELECT_COL"],
|
||||
["Other", "PANEL_BG_COL"],
|
||||
];
|
||||
|
||||
for (idxCategory in 0...themeColorOptions.length) {
|
||||
if (ui.panel(Id.handle().nest(idxCategory, {selected: true}), themeColorOptions[idxCategory][0])) {
|
||||
ui.indent();
|
||||
|
||||
var attributes = themeColorOptions[idxCategory].slice(1);
|
||||
|
||||
for (idxElemAttribs in 0...attributes.length) {
|
||||
var themeColorOption = attributes[idxElemAttribs];
|
||||
// is getField() better?
|
||||
ui.row([2/3, 1/3]);
|
||||
ui.text(themeColorOption);
|
||||
|
||||
var themeColor = Reflect.getProperty(Editor.selectedTheme, themeColorOption);
|
||||
|
||||
var handleCol = handleThemeColor.nest(Canvas.themes.indexOf(Editor.selectedTheme)).nest(idxCategory).nest(idxElemAttribs, {color: themeColor});
|
||||
var col = ui.colorField(handleCol, true);
|
||||
Reflect.setProperty(Editor.selectedTheme, themeColorOption, col);
|
||||
}
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ui.tab(htab, "Assets")) {
|
||||
if (ui.button("Import Asset")) {
|
||||
Editor.showFiles = true;
|
||||
Editor.foldersOnly = false;
|
||||
Editor.filesDone = function(path:String) {
|
||||
path = StringTools.rtrim(path);
|
||||
path = Path.toRelative(path, Main.cwd);
|
||||
Assets.importAsset(canvas, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (canvas.assets.length > 0) {
|
||||
ui.text("(Drag and drop assets to canvas)", zui.Zui.Align.Center);
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Imported Assets")) {
|
||||
ui.indent();
|
||||
|
||||
var i = canvas.assets.length - 1;
|
||||
while (i >= 0) {
|
||||
var asset = canvas.assets[i];
|
||||
if (!Assets.isPathFont(asset.name) && ui.image(Assets.getImage(asset)) == State.Started) {
|
||||
Editor.dragAsset = asset;
|
||||
} else if (Assets.isPathFont(asset.name)) {
|
||||
var oldFont = ui.ops.font;
|
||||
var oldFontSize = ui.fontSize;
|
||||
ui.ops.font = Assets.getFont(asset);
|
||||
ui.fontSize = Std.int(32 * ui.SCALE());
|
||||
ui.text(asset.name);
|
||||
ui.ops.font = oldFont;
|
||||
ui.fontSize = oldFontSize;
|
||||
}
|
||||
ui.row([7/8, 1/8]);
|
||||
asset.name = ui.textInput(Id.handle().nest(asset.id, {text: asset.name}), "", Right);
|
||||
Editor.assetNames[i + 1] = asset.name; // assetNames[0] == ""
|
||||
if (ui.button("X")) {
|
||||
Assets.getImage(asset).unload();
|
||||
canvas.assets.splice(i, 1);
|
||||
Editor.assetNames.splice(i + 1, 1);
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
}
|
||||
else ui.text("(Drag and drop images and fonts here)", zui.Zui.Align.Center);
|
||||
}
|
||||
|
||||
if (ui.tab(htab, "Preferences")) {
|
||||
if (ui.panel(Id.handle({selected: true}), "Application")) {
|
||||
ui.indent();
|
||||
|
||||
var hscale = Id.handle({value: 1.0});
|
||||
ui.slider(hscale, "UI Scale", 0.5, 4.0, true);
|
||||
if (hscale.changed && !ui.inputDown) {
|
||||
ui.setScale(hscale.value);
|
||||
Editor.windowW = Std.int(Editor.defaultWindowW * hscale.value);
|
||||
}
|
||||
|
||||
Main.prefs.window_vsync = ui.check(Id.handle({selected: true}), "VSync");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Grid")) {
|
||||
ui.indent();
|
||||
var gsize = Id.handle({value: 20});
|
||||
ui.slider(gsize, "Grid Size", 1, 128, true, 1);
|
||||
if (gsize.changed) {
|
||||
Editor.gridSize = Std.int(gsize.value);
|
||||
Editor.redrawGrid = true;
|
||||
}
|
||||
|
||||
Editor.gridSnapPos = ui.check(Id.handle({selected: true}), "Grid Snap Position");
|
||||
if (ui.isHovered) ui.tooltip("Snap the element's position to the grid");
|
||||
Editor.gridSnapBounds = ui.check(Id.handle({selected: false}), "Grid Snap Bounds");
|
||||
if (ui.isHovered) ui.tooltip("Snap the element's bounds to the grid");
|
||||
Editor.gridUseRelative = ui.check(Id.handle({selected: true}), "Use Relative Grid");
|
||||
if (ui.isHovered) ui.tooltip("Use a grid that's relative to the selected element");
|
||||
|
||||
Editor.useRotationSteps = ui.check(Id.handle({selected: false}), "Use Fixed Rotation Steps");
|
||||
if (ui.isHovered) ui.tooltip("Rotate elements by a fixed step size");
|
||||
var rotStepHandle = Id.handle({value: 15});
|
||||
if (Editor.useRotationSteps) {
|
||||
ui.slider(rotStepHandle, "Rotation Step Size", 1, 180, true, 1);
|
||||
if (rotStepHandle.changed) {
|
||||
Editor.rotationSteps = Math.toRadians(rotStepHandle.value);
|
||||
}
|
||||
}
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
// if (ui.button("Save")) {
|
||||
// #if kha_krom
|
||||
// Krom.fileSaveBytes("config.lnx", haxe.io.Bytes.ofString(haxe.Json.stringify(leenkx.data.Config.raw)).getData());
|
||||
// #end
|
||||
// }
|
||||
// ui.text("leenkx2d");
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Shortcuts")){
|
||||
ui.indent();
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Select");
|
||||
var selectMouseHandle = Id.handle({position: 0});
|
||||
ui.combo(selectMouseHandle, ["Left Click", "Right Click"], "");
|
||||
if (ui.isHovered) ui.tooltip("Mouse button used for element selection.");
|
||||
if (selectMouseHandle.changed) {
|
||||
Main.prefs.keyMap.selectMouseButton = ["Left", "Right"][selectMouseHandle.position];
|
||||
}
|
||||
|
||||
ui.separator(8, false);
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Grab");
|
||||
Main.prefs.keyMap.grabKey = ui.keyInput(Id.handle({value: KeyCode.G}), "Key");
|
||||
if (ui.isHovered) ui.tooltip("Key used for grabbing elements");
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Rotate");
|
||||
Main.prefs.keyMap.rotateKey = ui.keyInput(Id.handle({value: KeyCode.R}), "Key");
|
||||
if (ui.isHovered) ui.tooltip("Key used for rotating elements");
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Size");
|
||||
Main.prefs.keyMap.sizeKey = ui.keyInput(Id.handle({value: KeyCode.S}), "Key");
|
||||
if (ui.isHovered) ui.tooltip("Key used for resizing elements");
|
||||
|
||||
ui.separator(8, false);
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Precision Transform");
|
||||
Main.prefs.keyMap.slowMovement = ui.keyInput(Id.handle({value: KeyCode.Shift}), "Key");
|
||||
if (ui.isHovered) ui.tooltip("More precise transformations");
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Invert Grid");
|
||||
Main.prefs.keyMap.gridInvert = ui.keyInput(Id.handle({value: KeyCode.Control}), "Key");
|
||||
if (ui.isHovered) ui.tooltip("Invert the grid setting");
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.text("Invert Rel. Grid");
|
||||
Main.prefs.keyMap.gridInvertRelative = ui.keyInput(Id.handle({value: KeyCode.Alt}), "Key");
|
||||
if (ui.isHovered) ui.tooltip("Invert the relative grid setting");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Console")) {
|
||||
ui.indent();
|
||||
|
||||
//ui.text(lastTrace);
|
||||
ui.text("Mouse X: "+ ui.inputX);
|
||||
ui.text("Mouse Y: "+ ui.inputY);
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
lib/leenkx2d/Sources/lnx2d/ui/UIToolBar.hx
Normal file
84
lib/leenkx2d/Sources/lnx2d/ui/UIToolBar.hx
Normal file
@ -0,0 +1,84 @@
|
||||
package lnx2d.ui;
|
||||
|
||||
// Zui
|
||||
import zui.Id;
|
||||
import zui.Zui;
|
||||
import leenkx.ui.Canvas.TCanvas;
|
||||
import leenkx.ui.Canvas.ElementType;
|
||||
|
||||
// Editor
|
||||
import lnx2d.tools.CanvasTools;
|
||||
|
||||
class UIToolBar {
|
||||
|
||||
public static function renderToolbar(ui:Zui, cui:Zui, canvas: TCanvas, width:Int) {
|
||||
|
||||
if (ui.window(Id.handle(), 0, 0, width, kha.System.windowHeight())) {
|
||||
ui.text("Add Elements:");
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Basic")) {
|
||||
ui.indent();
|
||||
|
||||
drawToolbarItem(ui, cui, canvas, "Empty", Empty, "Create an empty element");
|
||||
drawToolbarItem(ui, cui, canvas, "Text", Text, "Create a text element");
|
||||
drawToolbarItem(ui, cui, canvas, "Image", Image, "Create an image element");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
// ui.button("VLayout");
|
||||
// ui.button("HLayout");
|
||||
if (ui.panel(Id.handle({selected: true}), "Buttons")){
|
||||
ui.indent();
|
||||
|
||||
drawToolbarItem(ui, cui, canvas, "Button", Button, "Create a button element");
|
||||
drawToolbarItem(ui, cui, canvas, "Check", Check, "Create a checkbox element");
|
||||
drawToolbarItem(ui, cui, canvas, "Radio", Radio, "Create a inline-radio element");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Inputs")){
|
||||
ui.indent();
|
||||
|
||||
drawToolbarItem(ui, cui, canvas, "Text Input", TextInput, "Create a text input element");
|
||||
drawToolbarItem(ui, cui, canvas, "Text Area", TextArea, "Create a text area element");
|
||||
drawToolbarItem(ui, cui, canvas, "Key Input", KeyInput, "Create a key input element");
|
||||
drawToolbarItem(ui, cui, canvas, "Combo Box", Combo, "Create a combo box element");
|
||||
drawToolbarItem(ui, cui, canvas, "Slider", Slider, "Create a slider element");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Shapes")){
|
||||
ui.indent();
|
||||
|
||||
drawToolbarItem(ui, cui, canvas, "Rect", Rectangle, "Create a rectangle shape element");
|
||||
drawToolbarItem(ui, cui, canvas, "Fill Rect", FRectangle, "Create a filled rectangle shape element");
|
||||
drawToolbarItem(ui, cui, canvas, "Circle", Circle, "Create a circle shape element");
|
||||
drawToolbarItem(ui, cui, canvas, "Fill Circle", FCircle, "Create a filled circle shape element");
|
||||
drawToolbarItem(ui, cui, canvas, "Triangle", Triangle, "Create a triangle shape element");
|
||||
drawToolbarItem(ui, cui, canvas, "Fill Triangle", FTriangle, "Create a filled triangle shape element");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
|
||||
if (ui.panel(Id.handle({selected: true}), "Progress Bars")){
|
||||
ui.indent();
|
||||
|
||||
drawToolbarItem(ui, cui, canvas, "RectPB", ProgressBar, "Create a rectangular progress bar");
|
||||
drawToolbarItem(ui, cui, canvas, "CircularPB", CProgressBar, "Create a circular progress bar");
|
||||
|
||||
ui.unindent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function drawToolbarItem(ui: Zui, cui: Zui, canvas: TCanvas, label: String, elemType: ElementType, tooltip: String) {
|
||||
if (ui.button(label)) {
|
||||
Editor.selectedElem = CanvasTools.makeElem(cui, canvas, elemType);
|
||||
}
|
||||
|
||||
if (ui.isHovered) ui.tooltip(tooltip);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user