move standalone projects into own directory

This commit is contained in:
2023-04-27 11:39:41 -06:00
parent 5d593e22b0
commit acda704057
369 changed files with 1170 additions and 5 deletions

View File

@@ -0,0 +1,19 @@
Copyright (c) 2021 Valentin Lemière <valentin@ibilon.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,71 @@
![](https://raw.github.com/HaxeGodot/godot/main/.github/logo.png)
[haxe externs](https://github.com/HaxeGodot/godot) | [editor plugin](https://github.com/HaxeGodot/editor-plugin) | [demo](https://github.com/HaxeGodot/squash-the-creeps-3d) | [api doc](https://haxegodot.github.io/godot/) | [discussions](https://github.com/HaxeGodot/godot/discussions)
# Godot Editor Haxe Support Plugin
Godot 3.3 engine editor plugin to help with Haxe development.
The plugin is still in alpha, open an [issue](https://github.com/HaxeGodot/editor-plugin/issues) for bug reports or feature requests.
## Installation
The plugin isn't yet available on the godot asset library, to install it you can either:
* [download this repository](https://github.com/HaxeGodot/editor-plugin/archive/refs/heads/main.zip) and extract it in the `addons/haxe` folder of your project
You need to remove the `editor-plugin-main` folder added by github: have `addons/haxe/plugin.cfg` not `addons/haxe/editor-plugin-main/plugin.cfg`
* add it as a submodule `git submodule add https://github.com/HaxeGodot/editor-plugin.git addons/haxe`
You need to enable the plugin by going in the Project -> Project Settings menu, Plugins tab, and checking the Enabled box for the Haxe plugin.
## Setup
Haxe support requires Godot C#, if it hasn't been setup click on Project -> Tools -> C# -> Create C# solution.
The plugin can setup by clicking on the Project -> Tools -> Haxe -> Setup menu.
This will check for the presence of the godot haxelib, update the C# solution and add a hxml.
If the project already contains some of these files the setup will be stopped.
You can also do a [manual setup](#manual-setup) if you want more control.
## Haxe scripts
You can add/load/remove a Haxe script on a node by clicking on it, and in the inspector in Node -> Script -> Haxe Script click on the resource box.
When creating or clicking Edit on an script it'll open in your editor. By default it is configured for VSCode, and can be changed in Project -> Project Settings -> Haxe -> External Editor. For now only `None` and `VSCode` are supported.
### Building
You need to build the Haxe code before launching your game, you can do that:
* by manually using the hxml `haxe build.hxml`
* through your editor
* directly in the Godot editor in the bottom tab Haxe -> Build Haxe Project
Note: The files in `scripts/` must all define their main type, for a file `Foo.hx` you must have the type `Foo`, otherwise compilation will fail.
## Manual setup
Example hxml:
```hxml
--cs build
--define net-ver=50
--define no-compilation
--define analyzer-optimize
--class-path scripts
--library godot
--macro godot.Godot.buildProject()
--dce full
```
Modify the `<PropertyGroups>` of the `csproj` file:
```xml
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFramework>netstandard2.1</TargetFramework>
```
## License
The plugin is MIT licensed.

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 227.27 227.2705"
height="14"
viewBox="0 0 14 14.000001"
width="14"
version="1.1"
id="svg30"
sodipodi:docname="haxe.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<metadata
id="metadata36">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs34" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview32"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="25.168247"
inkscape:cx="-2.6813108"
inkscape:cy="8.1202027"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g28"
inkscape:document-rotation="0" />
<g
transform="matrix(0.24008588,0,0,0.24008588,-29.864211,-268.49858)"
id="g28">
<path
d="m 153.54527,1125.6329 -16.33394,-1.4926 -12.82162,-5.7964 h 14.57745 l 14.57811,7.2819"
fill="#fbc712"
id="path2"
style="fill:#d6d6d6;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 153.54527,1125.6329 17.3111,-2.0052 11.84578,-5.2838 h -14.57926 l -14.57762,7.2819"
fill="#fbc712"
id="path4"
style="fill:#dbdbdb;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 153.54527,1169.3679 14.57761,1.7353 14.57927,5.5532 h -14.57926 l -14.57762,-7.2968"
fill="#f6871f"
id="path6"
style="fill:#d6d6d6;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 153.54527,1169.3679 -14.57812,1.5669 -14.57745,5.7216 h 14.57745 l 14.57812,-7.2968"
fill="#f15e22"
id="path8"
style="fill:#d1d1d1;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 131.67835,1147.5003 -2.15152,17.1164 -5.13712,12.0397 v -14.5774 l 7.28864,-14.587"
fill="#fcee23"
id="path10"
style="fill:#e5e5e5;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 131.67835,1147.5003 -1.63904,-15.6753 -5.6496,-13.4811 v 14.578 l 7.28864,14.5701"
fill="#fcee23"
id="path12"
style="fill:#ebebeb;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 175.41219,1147.5003 2.10355,-15.8462 5.18526,-13.3102 v 14.578 l -7.28881,14.5701"
fill="#f04923"
id="path14"
style="fill:#c7c7c7;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 175.41219,1147.5003 1.422,16.0914 5.86681,13.0647 v -14.5774 l -7.2888,-14.587"
fill="#f04923"
id="path16"
style="fill:#c2c2c2;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 124.38971,1118.3439 29.15556,7.289 v 21.8552 h -21.86692 l -7.28864,-29.1293"
fill="#f8b218"
id="path18"
style="fill:#e0e0e0;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 124.38971,1176.6564 7.28864,-29.1561 21.86692,-0.012 v 21.8632 l -29.15555,7.2753"
fill="#f89d1c"
id="path20"
style="fill:#dbdbdb;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 182.70216,1118.3439 -7.28997,29.1554 -21.86692,-0.011 v -21.8563 l 29.15688,-7.273"
fill="#f37321"
id="path22"
style="fill:#d1d1d1;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 182.70216,1176.6564 -29.15689,-7.2885 v -21.8798 l 21.86693,0.012 7.28997,29.1263"
fill="#f15e22"
id="path24"
style="fill:#cccccc;fill-opacity:1;stroke-width:0.0813402" />
<path
d="m 153.54527,1125.6329 -21.86692,21.8674 21.86692,21.8676 21.86693,-21.8686 -21.86693,-21.8735"
fill="#f6871f"
id="path26"
style="fill:#d6d6d6;fill-opacity:1;stroke-width:0.0813402" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,7 @@
[plugin]
name="Haxe"
description="Haxe support for the Godot engine."
author="ibilon"
version="0.1.0"
script="scripts/haxe.gd"

View File

@@ -0,0 +1,14 @@
[gd_scene format=2]
[node name="About" type="AcceptDialog"]
margin_right = 83.0
margin_bottom = 58.0
window_title = "About Haxe support"
dialog_text = "This plugin adds support for the Haxe programming language.
Haxe support is in alpha.
Discuss usage at: https://github.com/HaxeGodot/godot/discussions
Report bugs about the editor plugin at: https://github.com/HaxeGodot/editor-plugin/issues/
Report bugs about the code support at: https://github.com/HaxeGodot/godot/issues/
"

View File

@@ -0,0 +1,47 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/haxe/scripts/building.gd" type="Script" id=1]
[sub_resource type="StyleBoxFlat" id=1]
bg_color = Color( 0.219608, 0.211765, 0.227451, 1 )
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color( 0.270588, 0.270588, 0.270588, 1 )
corner_radius_top_left = 4
[node name="Control" type="Control"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -152.0
margin_top = -38.5
margin_right = 152.0
margin_bottom = 38.5
script = ExtResource( 1 )
[node name="Background" type="Panel" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
custom_styles/panel = SubResource( 1 )
[node name="Label" type="Label" parent="."]
anchor_right = 1.0
margin_bottom = 26.0
text = "Building Haxe project..."
align = 1
valign = 1
autowrap = true
clip_text = true
[node name="ProgressBar" type="ProgressBar" parent="."]
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
margin_left = 11.0
margin_top = -6.5
margin_right = -11.0
margin_bottom = 7.5
max_value = 1.0

View File

@@ -0,0 +1,130 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/haxe/scripts/new_script.gd" type="Script" id=1]
[node name="NewScript" type="WindowDialog"]
margin_right = 350.0
margin_bottom = 270.0
size_flags_horizontal = 3
size_flags_vertical = 3
window_title = "Attach Node Haxe Script"
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="MarginContainer" type="MarginContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 10.0
margin_top = 10.0
margin_right = -10.0
margin_bottom = -10.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
margin_right = 330.0
margin_bottom = 250.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="GridContainer" type="GridContainer" parent="MarginContainer/VBoxContainer"]
margin_right = 330.0
margin_bottom = 52.0
columns = 2
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ClassLabel" type="Label" parent="MarginContainer/VBoxContainer/GridContainer"]
margin_top = 5.0
margin_right = 54.0
margin_bottom = 19.0
text = "Inherits:"
[node name="ClassValue" type="LineEdit" parent="MarginContainer/VBoxContainer/GridContainer"]
margin_left = 58.0
margin_right = 330.0
margin_bottom = 24.0
size_flags_horizontal = 3
text = "VBoxContainer"
[node name="PathLabel" type="Label" parent="MarginContainer/VBoxContainer/GridContainer"]
margin_top = 33.0
margin_right = 54.0
margin_bottom = 47.0
text = "Path:"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Path" type="HBoxContainer" parent="MarginContainer/VBoxContainer/GridContainer"]
margin_left = 58.0
margin_top = 28.0
margin_right = 330.0
margin_bottom = 52.0
[node name="PathValue" type="LineEdit" parent="MarginContainer/VBoxContainer/GridContainer/Path"]
margin_right = 256.0
margin_bottom = 24.0
size_flags_horizontal = 3
text = "res://scripts/VBoxContainer.hx"
[node name="Load" type="Button" parent="MarginContainer/VBoxContainer/GridContainer/Path"]
margin_left = 260.0
margin_right = 272.0
margin_bottom = 24.0
[node name="PaddingTop" type="Control" parent="MarginContainer/VBoxContainer"]
margin_top = 56.0
margin_right = 330.0
margin_bottom = 66.0
rect_min_size = Vector2( 0, 10 )
[node name="TextEdit" type="RichTextLabel" parent="MarginContainer/VBoxContainer"]
margin_top = 70.0
margin_right = 330.0
margin_bottom = 212.0
rect_min_size = Vector2( 0, 110 )
size_flags_vertical = 3
bbcode_enabled = true
scroll_active = false
[node name="PaddingBottom" type="Control" parent="MarginContainer/VBoxContainer"]
margin_top = 216.0
margin_right = 330.0
margin_bottom = 226.0
rect_min_size = Vector2( 0, 10 )
[node name="Buttons" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
margin_top = 230.0
margin_right = 330.0
margin_bottom = 250.0
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Left" type="Button" parent="MarginContainer/VBoxContainer/Buttons"]
margin_left = 92.0
margin_right = 146.0
margin_bottom = 20.0
text = "Cancel"
[node name="Padding" type="Control" parent="MarginContainer/VBoxContainer/Buttons"]
margin_left = 150.0
margin_right = 180.0
margin_bottom = 20.0
rect_min_size = Vector2( 30, 0 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Right" type="Button" parent="MarginContainer/VBoxContainer/Buttons"]
margin_left = 184.0
margin_right = 237.0
margin_bottom = 20.0
text = "Create"

View File

@@ -0,0 +1,35 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/haxe/scripts/tab.gd" type="Script" id=1]
[node name="Tab" type="Control"]
margin_right = 40.0
margin_bottom = 40.0
rect_min_size = Vector2( 0, 200 )
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="VBoxContainer" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 0, 200 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Button" type="Button" parent="VBoxContainer"]
margin_right = 130.0
margin_bottom = 20.0
size_flags_horizontal = 0
text = "Build Haxe Project"
[node name="TextLog" type="TextEdit" parent="VBoxContainer"]
margin_top = 24.0
margin_right = 130.0
margin_bottom = 200.0
size_flags_horizontal = 3
size_flags_vertical = 3
readonly = true
context_menu_enabled = false

View File

@@ -0,0 +1,182 @@
import sys.io.Process;
import haxe.xml.Access;
import sys.FileSystem;
import sys.io.File;
using StringTools;
class Setup {
public static function main() {
// Checking haxelib for godot externs.
final haxelibCheck = new Process("haxelib", ["path", "godot"]);
if (haxelibCheck.exitCode() != 0) {
Sys.print("haxelib");
return;
}
// Find unique csproj file.
var csproj = null;
for (entry in FileSystem.readDirectory(".")) {
if (FileSystem.isDirectory(entry)) {
continue;
}
if (entry.endsWith(".csproj")) {
if (csproj != null) {
Sys.print("multiple_csproj");
return;
}
csproj = entry;
}
}
if (csproj == null) {
Sys.print("csproj");
return;
}
// Dirty check.
final dirty = ["build.hxml", "build/", "scripts/"].filter(entry -> FileSystem.exists(entry));
if (dirty.length != 0) {
Sys.print("dirty:" + dirty.join(" "));
return;
}
// Update csproj file.
final csprojData = new Access(Xml.parse(File.getContent(csproj)));
final propertyGroup = csprojData.node.Project.node.PropertyGroup;
for (property in propertyGroup.elements) {
switch (property.name) {
case "AllowUnsafeBlocks", "TargetFramework":
propertyGroup.x.removeChild(property.x);
default:
}
}
propertyGroup.x.addChild(Xml.parse("<AllowUnsafeBlocks>true</AllowUnsafeBlocks>"));
propertyGroup.x.addChild(Xml.parse("<TargetFramework>netstandard2.1</TargetFramework>"));
File.saveContent(csproj, XmlPrinter.print(csprojData.x));
// Create project.
FileSystem.createDirectory("scripts");
File.saveContent("scripts/import.hx", "import godot.*;\nimport godot.GD.*;\n\nusing godot.Utils;\n");
File.saveContent("build.hxml", "--cs build\n--define net-ver=50\n--define no-compilation\n--define analyzer-optimize\n--class-path scripts\n--library godot\n--macro godot.Godot.buildProject()\n--dce full\n");
final ret = Sys.command("haxe", ["build.hxml"]);
if (ret == 0) {
Sys.print("ok");
}
}
}
// Modified version of haxe.xml.Printer
class XmlPrinter {
static public function print(xml:Xml) {
final printer = new XmlPrinter();
printer.writeNode(xml, "");
return printer.output.toString();
}
var output:StringBuf;
function new() {
output = new StringBuf();
}
function writeNode(value:Xml, indent:String) {
switch (value.nodeType) {
case CData:
write(indent + "<![CDATA[");
write(value.nodeValue);
write("]]>");
newline();
case Comment:
var commentContent = value.nodeValue;
commentContent = ~/[\n\r\t]+/g.replace(commentContent, "");
commentContent = "<!--" + commentContent + "-->";
write(indent);
write(StringTools.trim(commentContent));
newline();
case Document:
for (child in value) {
writeNode(child, indent);
}
case Element:
write(indent + "<");
write(value.nodeName);
for (attribute in value.attributes()) {
write(" " + attribute + "=\"");
write(StringTools.htmlEscape(value.get(attribute), true));
write("\"");
}
if (hasChildren(value)) {
final textOnly = hasTextOnly(value);
write(">");
if (!textOnly) {
newline();
}
for (child in value) {
writeNode(child, textOnly ? "" : (indent + " "));
}
write((textOnly ? "" : indent) + "</");
write(value.nodeName);
write(">");
newline();
} else {
write("/>");
newline();
}
case PCData:
final nodeValue = value.nodeValue.trim();
if (nodeValue.length != 0) {
write(indent + StringTools.htmlEscape(nodeValue));
}
case ProcessingInstruction:
write("<?" + value.nodeValue + "?>");
newline();
case DocType:
write("<!DOCTYPE " + value.nodeValue + ">");
newline();
}
}
inline function write(input:String) {
output.add(input);
}
inline function newline() {
output.add("\n");
}
function hasTextOnly(value:Xml):Bool {
for (child in value) {
switch (child.nodeType) {
case PCData:
default:
return false;
}
}
return true;
}
function hasChildren(value:Xml):Bool {
for (child in value) {
switch (child.nodeType) {
case Element, PCData:
return true;
case CData, Comment:
if (StringTools.ltrim(child.nodeValue).length != 0) {
return true;
}
case _:
}
}
return false;
}
}

View File

@@ -0,0 +1,16 @@
tool
class_name Building
extends Control
func build_haxe_project():
print("Building haxe project...");
var res = OS.execute("haxe", ["build.hxml"], true);
$ProgressBar.value = 1
yield(VisualServer, 'frame_post_draw')
print("Project builded with code: ", res)
queue_free()

View File

@@ -0,0 +1,6 @@
tool
class_name HaxePluginConstants
const SETTING_HIDE_NATIVE_SCRIPT_FIELD := "haxe/hide_native_script_field"
const SETTING_EXTERNAL_EDITOR := "haxe/external_editor"
const BUILD_ON_PLAY := "haxe/build_on_play"

View File

@@ -0,0 +1,164 @@
tool
class_name HaxePluginEditorProperty
extends EditorProperty
var haxe_icon := preload("res://addons/haxe/icons/haxe.svg")
var new_script_dialog := preload("res://addons/haxe/scenes/new_script.tscn")
var base:Control
var object:Node
var script_name := ""
var script_path := ""
var b:MenuButton
var b2:MenuButton
func setup(base:Control, object:Node) -> void:
self.base = base
self.object = object
label = "Haxe Script"
var h := HBoxContainer.new()
# TODO revert icon
b = MenuButton.new()
b.flat = true
h.add_child(b)
b2 = MenuButton.new()
b2.flat = true
b2.icon = base.get_icon("GuiDropdown", "EditorIcons")
h.add_child(b2)
add_child(h)
update_property()
func setup_menu(base:Control, button:MenuButton, has_script:bool) -> void:
if not button.is_connected("gui_input", self, "on_menu_gui"):
button.connect("gui_input", self, "on_menu_gui")
var menu := button.get_popup()
for i in range(menu.get_item_count()):
menu.remove_item(0)
if not has_script:
menu.add_icon_item(base.get_icon("ScriptCreate", "EditorIcons"), "New Haxe Script")
else:
menu.add_icon_item(base.get_icon("ScriptRemove", "EditorIcons"), "Remove Haxe Script")
menu.add_icon_item(base.get_icon("Load", "EditorIcons"), "Load Haxe Script")
if has_script:
menu.add_icon_item(base.get_icon("Edit", "EditorIcons"), "Edit")
if not menu.is_connected("index_pressed", self, "on_popup_select"):
menu.connect("index_pressed", self, "on_popup_select", [has_script])
func on_menu_gui(event:InputEvent) -> void:
# If is right click then pretend it's a left click
if event is InputEventMouseButton and event.pressed and event.button_index == 2:
event.button_index = 1
func on_popup_select(id:int, has_script:bool) -> void:
if id == 0: # New/Remove
if not has_script: # New
var dialog := new_script_dialog.instance()
dialog.setup(base, object.get_class(), object.get_path().get_name(object.get_path().get_name_count() - 1))
dialog.theme = base.theme
dialog.connect("create", self, "on_create")
base.add_child(dialog)
dialog.popup_centered()
else: # Remove
object.remove_meta("haxe_script")
object.set_script(null)
elif id == 1: # Load
var dialog := EditorFileDialog.new()
base.add_child(dialog)
dialog.access = EditorFileDialog.ACCESS_RESOURCES
dialog.current_dir = "res://scripts/"
dialog.mode = EditorFileDialog.MODE_OPEN_FILE
dialog.theme = base.theme
dialog.add_filter("*.hx ; Haxe script")
dialog.connect("file_selected", self, "on_load_file")
dialog.popup_centered_ratio()
elif id == 2: # Edit
open_file(script_path)
else:
print("Unknown entry: ", id)
func on_create(is_load:bool, class_value:String, path_value:String) -> void:
if not is_load:
var f := path_value.find_last("/")
var name := path_value.substr(f + 1, path_value.find_last(".hx") - f - 1)
var d := path_value.substr(14).split("/")
d.remove(d.size() - 1)
var pack := d.join(".")
if not pack.empty():
pack = " " + pack;
if class_value == name:
class_value = "godot." + class_value
var file := File.new()
file.open(path_value, File.WRITE)
file.store_string("package" + pack + ";\n\nclass " + name + " extends " + class_value + " {\n}\n")
file.close()
open_file(path_value)
on_load_file(path_value)
func open_file(path:String) -> void:
var editor:String = ProjectSettings.get(HaxePluginConstants.SETTING_EXTERNAL_EDITOR)
if editor == "None":
pass
elif editor == "VSCode":
OS.execute("code", [ProjectSettings.globalize_path(path)], false)
else:
print("Unknown external editor: " + editor)
func on_load_file(path:String) -> void:
object.set_meta("haxe_script", path)
var cs_path := path.replace("res://scripts", "")
var p := cs_path.find_last("/")
var name := cs_path.substr(p, cs_path.length() - 2 - p) + "cs"
cs_path = "build/src" + cs_path.substr(0, p)
var d := Directory.new()
d.make_dir_recursive(cs_path)
var file_path := "res://" + cs_path + name
var cs_file := File.new()
if not cs_file.file_exists(file_path):
cs_file.open(file_path, File.WRITE)
cs_file.store_string("\n")
cs_file.close()
object.set_script(load(file_path))
func update_property() -> void:
var script_name := "[empty]"
if object.has_meta("haxe_script"):
if not object.get_script():
object.remove_meta("haxe_script")
else:
script_path = object.get_meta("haxe_script")
var p := script_path.find_last("/")
script_name = script_path.substr(p + 1)
var has_script := script_path != ""
b.size_flags_horizontal = MenuButton.SIZE_EXPAND_FILL
if has_script:
b.icon = haxe_icon
b.text = script_name
b.hint_tooltip = script_path
setup_menu(base, b, has_script)
setup_menu(base, b2, has_script)
func get_tooltip_text() -> String:
return "Haxe Script"

View File

@@ -0,0 +1,103 @@
tool
class_name HaxePlugin
extends EditorPlugin
var about_dialog := preload("res://addons/haxe/scenes/about.tscn")
var tab := preload("res://addons/haxe/scenes/tab.tscn").instance()
var build_dialog := preload("res://addons/haxe/scenes/building.tscn")
var inspector_plugin:HaxePluginInspectorPlugin
func _enter_tree() -> void:
var base := get_editor_interface().get_base_control()
# Init
setup_settings()
# Inspector plugin
inspector_plugin = HaxePluginInspectorPlugin.new()
inspector_plugin.setup(base)
add_inspector_plugin(inspector_plugin)
# Tool menu entry
var menu := PopupMenu.new()
menu.add_item("About")
menu.add_item("Setup")
menu.connect("index_pressed", self, "on_menu")
add_tool_submenu_item("Haxe", menu)
# Bottom dock tab
tab.setup(base)
add_control_to_bottom_panel(tab, "Haxe")
func _exit_tree() -> void:
# TODO tab.gd still leaks?
remove_control_from_bottom_panel(tab)
tab.queue_free()
remove_tool_menu_item("Haxe")
remove_inspector_plugin(inspector_plugin)
func setup_settings() -> void:
if not ProjectSettings.has_setting(HaxePluginConstants.SETTING_HIDE_NATIVE_SCRIPT_FIELD):
ProjectSettings.set_setting(HaxePluginConstants.SETTING_HIDE_NATIVE_SCRIPT_FIELD, true)
if not ProjectSettings.has_setting(HaxePluginConstants.SETTING_EXTERNAL_EDITOR):
ProjectSettings.set_setting(HaxePluginConstants.SETTING_EXTERNAL_EDITOR, "VSCode")
ProjectSettings.add_property_info({
"name": HaxePluginConstants.SETTING_EXTERNAL_EDITOR,
"type": TYPE_STRING,
"hint": PROPERTY_HINT_ENUM,
"hint_string": "None,VSCode"
});
if not ProjectSettings.has_setting(HaxePluginConstants.BUILD_ON_PLAY):
ProjectSettings.set_setting(HaxePluginConstants.BUILD_ON_PLAY, false)
func on_menu(id:int) -> void:
var theme := get_editor_interface().get_base_control().theme
if id == 0: # About
var dialog := about_dialog.instance()
add_child(dialog)
dialog.theme = theme
dialog.popup_centered()
elif id == 1: # Setup
var output := []
OS.execute("haxe", ["--class-path", "addons/haxe/scripts", "--run", "Setup"], true, output, true)
var dialog := AcceptDialog.new()
add_child(dialog)
if output.size() != 1:
dialog.dialog_text = "Unknown error:\n" + PoolStringArray(output).join("\n")
elif "command not found" in output[0].to_lower():
dialog.dialog_text = "Haxe command not found."
elif output[0] == "haxelib":
dialog.dialog_text = "Godot externs not found.\nRun 'haxelib install godot' first."
elif output[0] == "multiple_csproj":
dialog.dialog_text = "Multiple C# solutions found.\nCannot setup."
elif output[0] == "csproj":
dialog.dialog_text = "C# solution not found (.csproj file).\nYou need to setup Godot Mono first:\nProject -> Tools -> Mono -> Create C# solution."
elif output[0].begins_with("dirty:"):
dialog.dialog_text = "Project already contains: " + output[0].substr(6) + "\nTo avoid data loss the setup wasn't run."
elif output[0] == "ok":
dialog.dialog_text = "Setup successful."
else:
dialog.dialog_text = "Unknown error: " + output[0]
dialog.theme = theme
dialog.window_title = "Haxe Setup"
dialog.popup_centered()
else:
print("Unknown menu: ", id)
func _input(event):
if event is InputEventKey and ProjectSettings.get_setting(HaxePluginConstants.BUILD_ON_PLAY):
if event.scancode == KEY_F5 or event.scancode == KEY_F6 and event.echo:
var dialog = build_dialog.instance()
add_child(dialog)
yield(VisualServer, 'frame_post_draw')
dialog.call("build_haxe_project")

View File

@@ -0,0 +1,22 @@
tool
class_name HaxePluginInspectorPlugin
extends EditorInspectorPlugin
var base:Control
func setup(base:Control) -> void:
self.base = base
#warning-ignore:unused_argument
func can_handle(object:Object) -> bool:
return true
#warning-ignore:unused_argument
func parse_property(object:Object, type:int, path:String, hint:int, hint_text:String, usage:int) -> bool:
if object is Node and type == TYPE_OBJECT and path == "script":
var e := HaxePluginEditorProperty.new()
e.setup(base, object)
add_custom_control(e)
return ProjectSettings.get_setting(HaxePluginConstants.SETTING_HIDE_NATIVE_SCRIPT_FIELD)
return false

View File

@@ -0,0 +1,157 @@
tool
extends WindowDialog
signal create(is_load, class_value, path_value)
var base:Control
var cancel_button:Button
var create_button:Button
var class_valid := true
var path_valid := true
var name_valid := true
var name_warning := false
var extension_valid := true
var is_load := false
var class_value := ""
var path_value := ""
func setup(base:Control, class_value:String, name:String) -> void:
self.base = base
var left := $MarginContainer/VBoxContainer/Buttons/Left
var right := $MarginContainer/VBoxContainer/Buttons/Right
if OS.get_name() == "Windows" or OS.get_name() == "UWP":
setup_buttons(right, left)
else:
setup_buttons(left, right)
$MarginContainer/VBoxContainer/GridContainer/ClassValue.connect("text_changed", self, "on_class")
$MarginContainer/VBoxContainer/GridContainer/Path/PathValue.connect("text_changed", self, "on_path")
var path_button := $MarginContainer/VBoxContainer/GridContainer/Path/Load
path_button.icon = base.get_icon("Folder", "EditorIcons")
path_button.connect("button_down", self, "on_folder")
on_class(class_value)
on_path("res://scripts/" + name.substr(0, 1).to_upper() + name.substr(1) + ".hx")
func setup_buttons(cancel:Button, create:Button) -> void:
cancel.text = "Cancel"
cancel.connect("button_down", self, "on_cancel")
cancel_button = cancel
create.text = "Create"
create.connect("button_down", self, "on_create")
create_button = create
func on_cancel() -> void:
hide()
func on_create() -> void:
hide()
emit_signal("create", is_load, class_value, path_value)
func on_class(value:String) -> void:
class_value = value
class_valid = ClassDB.class_exists(value) and ClassDB.can_instance(value)
revalidate()
func on_folder() -> void:
var file := path_value
file = file.substr(file.find_last("/") + 1)
var dialog := EditorFileDialog.new()
base.add_child(dialog)
dialog.access = EditorFileDialog.ACCESS_RESOURCES
dialog.current_dir = "res://scripts/"
dialog.current_file = file
dialog.disable_overwrite_warning = true
dialog.theme = base.theme
dialog.window_title = "Open Haxe Script / Choose Location"
dialog.add_filter("*.hx ; Haxe script")
dialog.connect("file_selected", self, "on_path")
dialog.get_ok().text = "Open"
dialog.popup_centered_ratio()
func on_path(fullpath:String) -> void:
path_value = fullpath
var dir_p := fullpath.find_last("/")
var ext_p := fullpath.find_last(".")
var path := ""
var file := ""
if dir_p < ext_p:
path = fullpath.substr(0, dir_p)
file = fullpath.substr(dir_p + 1)
else:
path = fullpath
var d := Directory.new()
var f := File.new()
is_load = f.file_exists(path_value)
extension_valid = file.ends_with(".hx")
name_valid = ext_p < fullpath.length() - 1 and ext_p > dir_p + 1
name_warning = name_valid && extension_valid && isBuiltin(file.substr(0, file.length() - 3))
path_valid = path.begins_with("res://") and d.dir_exists(path)
revalidate()
func isBuiltin(name:String) -> bool:
var haxeGodotBuiltins = ["Action", "CustomSignal", "CustomSignalUsings", "Godot", "Nullable1", "Signal", "SignalUsings", "Utils"]
return ClassDB.class_exists(name) or haxeGodotBuiltins.has(name)
func revalidate() -> void:
var text_edit := $MarginContainer/VBoxContainer/TextEdit
text_edit.bbcode_text = ""
var valid_color := Color(0.062775, 0.730469, 0.062775)
var error_color := Color(0.820312, 0.028839, 0.028839)
var warning_color := Color(0.9375, 0.537443, 0.06958)
if not class_valid:
text_edit.push_color(error_color)
text_edit.append_bbcode("- Invalid inherited parent name.\n\n")
text_edit.pop()
elif not extension_valid:
text_edit.push_color(error_color)
text_edit.append_bbcode("- Invalid extension.\n\n")
text_edit.pop()
elif not path_valid:
text_edit.push_color(error_color)
text_edit.append_bbcode("- Invalid path.\n\n")
text_edit.pop()
elif not name_valid:
text_edit.push_color(error_color)
text_edit.append_bbcode("- Invalid filename.\n\n")
text_edit.pop()
else:
text_edit.push_color(valid_color)
text_edit.append_bbcode("- Haxe script path is valid.\n\n")
if is_load:
text_edit.append_bbcode("- Will load an existing Haxe script.\n\n")
else:
text_edit.append_bbcode("- Will create a new Haxe script.\n\n")
text_edit.pop()
if name_warning:
text_edit.push_color(warning_color)
text_edit.append_bbcode("Warning: Having the script name be the same as a built-in type is usually not desired.\n\n")
text_edit.pop()
var class_edit:LineEdit = $MarginContainer/VBoxContainer/GridContainer/ClassValue
var class_edit_column := class_edit.caret_position
class_edit.text = class_value
class_edit.caret_position = class_edit_column if class_edit_column <= class_value.length() else class_value.length()
var path_edit:LineEdit = $MarginContainer/VBoxContainer/GridContainer/Path/PathValue
var path_edit_column := path_edit.caret_position
path_edit.text = path_value
path_edit.caret_position = path_edit_column if path_edit_column <= path_value.length() else path_value.length()

View File

@@ -0,0 +1,67 @@
tool
extends Control
onready var button := $VBoxContainer/Button
onready var text_log := $VBoxContainer/TextLog
var base:Control
var icon := 0
var icons := []
var mutex := Mutex.new()
var output := []
var time := 0.0
var thread:Thread = null
func setup(base:Control) -> void:
self.base = base
for i in range(8):
icons.append(base.get_icon("Progress%s"%(i + 1), "EditorIcons"))
func _ready() -> void:
button.connect("button_down", self, "build_haxe_project")
func build_haxe_project() -> void:
if thread != null:
return
thread = Thread.new()
button.icon = icons[0]
button.text = "Building Haxe Project ..."
icon = 0
text_log.text = ""
time = 0.0
output = []
thread.start(self, "run_thread")
func _process(delta:float) -> void:
if thread != null:
update_log()
time += delta
if time > 0.1:
time = 0
icon = (icon + 1) % 8
button.icon = icons[icon]
func run_thread(userdata) -> void:
var ret := OS.execute("haxe", ["build.hxml"], true, output, true)
update_log()
button.icon = base.get_icon("StatusSuccess" if ret == 0 else "StatusError", "EditorIcons")
button.text = "Build Haxe Project"
call_deferred("end_thread")
func end_thread() -> void:
thread.wait_to_finish()
thread = null
func update_log() -> void:
mutex.lock()
text_log.text = PoolStringArray(output).join("\n")
mutex.unlock()
func _exit_tree():
if thread != null:
thread.wait_to_finish()
thread = null