/*
* Copyright (c) 2012-2026 Daniele Bartolini et al.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Crown
{
public InputEnum make_deploy_config_combo()
{
string? labels[] =
{
TargetConfig.RELEASE.to_label(),
TargetConfig.DEVELOPMENT.to_label(),
#if CROWN_DEBUG
TargetConfig.DEBUG.to_label(),
#endif
};
string? ids[] =
{
((int)TargetConfig.RELEASE).to_string(),
((int)TargetConfig.DEVELOPMENT).to_string(),
#if CROWN_DEBUG
((int)TargetConfig.DEBUG).to_string(),
#endif
};
return new InputEnum(ids[0], labels, ids);
}
public Gtk.Button make_deploy_button(TargetPlatform platform)
{
var btn = new Gtk.Button();
btn.label = "Package Project for %s".printf(platform.to_label());
btn.margin_start = 12;
btn.margin_end = 12;
btn.margin_top = 12;
btn.get_style_context().add_class("suggested-action");
return btn;
}
public delegate int DeployerCheckConfig();
public class DeployerPage : Gtk.Box
{
public Gtk.Stack _stack;
public Gtk.Box _check_config_box;
public Gtk.Widget _deployer_options;
public unowned DeployerCheckConfig _check_config;
public DeployerPage(TargetPlatform target_platform, Gtk.Widget deployer_options, DeployerCheckConfig? check_config = null)
{
Object(orientation: Gtk.Orientation.VERTICAL);
_stack = new Gtk.Stack();
_deployer_options = deployer_options;
_check_config = check_config;
string h1 = "Not configured";
string p1 = "The %s deployer has not been configured yet".printf(target_platform.to_label());
string p2 = "Follow the instructions to get started";
var h1l = new Gtk.Label(null);
h1l.set_markup(h1);
h1l.valign = Gtk.Align.CENTER;
var p1l = new Gtk.Label(null);
p1l.set_markup(p1);
p1l.valign = Gtk.Align.CENTER;
var p2l = new Gtk.Label(null);
p2l.get_style_context().add_class("colorfast-link");
p2l.set_markup(p2);
p2l.valign = Gtk.Align.CENTER;
p2l.activate_link.connect(() => {
try {
string CROWN_DEPLOY_URL = CROWN_LATEST_DOCS_URL + "/deploying";
string CROWN_DEPLOY_CONFIG_URL = CROWN_DEPLOY_URL + "/%s.html".printf(target_platform.to_key());
AppInfo.launch_default_for_uri(CROWN_DEPLOY_CONFIG_URL, null);
} catch (Error e) {
loge(e.message);
}
return true;
});
_check_config_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 12);
_check_config_box.valign = Gtk.Align.CENTER;
_check_config_box.pack_start(h1l);
_check_config_box.pack_start(p1l);
_check_config_box.pack_start(p2l);
_stack.add(_check_config_box);
_stack.add(_deployer_options);
this.pack_start(_stack);
this.map.connect(on_map);
}
public void on_map()
{
if (_check_config != null) {
if (_check_config() != 0)
_stack.set_visible_child(_check_config_box);
else
_stack.set_visible_child(_deployer_options);
} else {
_stack.set_visible_child(_deployer_options);
}
}
}
#if CROWN_PLATFORM_WINDOWS
public bool can_build_32bit_arm = false;
#else
public bool can_build_32bit_arm = true;
#endif
public class DeployDialog : Gtk.Window
{
public RuntimeInstance _editor;
public Project _project;
// Android page.
public Gtk.Button _android_deploy_button;
public InputFile _android_output_path;
public InputEnum _android_config;
public InputBool _android_armv7;
public InputBool _android_armv8;
public InputBool _android_use_debug_keystore;
public InputFile _android_keystore;
public Gtk.Entry _android_keystore_password;
public Gtk.Entry _android_key_alias;
public Gtk.Entry _android_key_password;
public InputString _android_app_title;
public Gtk.Entry _android_app_identifier;
public Gtk.Entry _android_app_version_code;
public Gtk.Entry _android_app_version_name;
public PropertyGridSet _android_set;
public Gtk.Box _android_box;
public AndroidDeployer _android;
public DeployerPage _android_page;
// HTML5 page.
public Gtk.Button _html5_deploy_button;
public InputFile _html5_output_path;
public InputEnum _html5_config;
public InputString _html5_app_title;
public PropertyGridSet _html5_set;
public Gtk.Box _html5_box;
public HTML5Deployer _html5;
public DeployerPage _html5_page;
// Linux page.
public Gtk.Button _linux_deploy_button;
public InputFile _linux_output_path;
public InputEnum _linux_config;
public InputString _linux_app_title;
public PropertyGridSet _linux_set;
public Gtk.Box _linux_box;
public DeployerPage _linux_page;
// Windows page.
public Gtk.Button _windows_deploy_button;
public InputFile _windows_output_path;
public InputEnum _windows_config;
public InputString _windows_app_title;
public PropertyGridSet _windows_set;
public Gtk.Box _windows_box;
public DeployerPage _windows_page;
public Gtk.Notebook _notebook;
public Gtk.EventControllerKey _controller_key;
public DeployDialog(Project project, RuntimeInstance editor)
{
this.title = "Deploy";
this.set_icon_name(CROWN_EDITOR_ICON_NAME);
_project = project;
_editor = editor;
// Android page.
_android_deploy_button = make_deploy_button(TargetPlatform.ANDROID);
_android_deploy_button.clicked.connect(() => {
// Validate input fields.
string? output_path = _android_output_path.value;
if (output_path == null) {
loge("Select a valid output Destination");
return;
}
string app_title = _android_app_title.value.strip();
if (app_title.length == 0) {
loge("Enter a valid App Title");
return;
}
string app_identifier = _android_app_identifier.text.strip();
if (app_title.length == 0 || app_identifier.split(".").length != 3) {
loge("Enter a valid App Identifier");
return;
}
int app_version_code;
if (int.try_parse(_android_app_version_code.text, out app_version_code) == false) {
loge("Enter a valid App Version Code");
return;
}
string app_version_name = _android_app_version_name.text.strip();
if (app_version_name.length == 0) {
loge("Enter a valid App Version Name");
return;
}
string? keystore_path = _android_use_debug_keystore.value
? GLib.Path.build_filename(GLib.Environment.get_home_dir(), ".android", "debug.keystore")
: _android_keystore.value
;
if (keystore_path == null) {
loge("Enter a valid Keystore file");
return;
}
string keystore_pass = _android_use_debug_keystore.value
? "android"
: _android_keystore_password.text
;
if (keystore_path.length == 0) {
loge("Enter a valid Keystore Password");
return;
}
string key_alias = _android_use_debug_keystore.value
? "androiddebugkey"
: _android_key_alias.text
;
if (key_alias.length == 0) {
loge("Enter a valid Key Alias");
return;
}
string key_pass = _android_use_debug_keystore.value
? "android"
: _android_key_password.text
;
if (key_pass.length == 0) {
loge("Enter a valid Key Password");
return;
}
TargetArch[] archs =
{
TargetArch.ARM,
TargetArch.ARM64
};
for (int ii = 0; ii < archs.length; ++ii) {
if (archs[ii] == TargetArch.ARM && !_android_armv7.value)
continue;
if (archs[ii] == TargetArch.ARM64 && !_android_armv8.value)
continue;
// Create the package.
GLib.Variant paramz[] =
{
(string)output_path,
int.parse(_android_config.value),
app_title,
app_identifier,
app_version_code,
app_version_name,
keystore_path,
keystore_pass,
key_alias,
key_pass,
archs[ii]
};
GLib.Application.get_default().activate_action("create-package-android"
, new GLib.Variant.tuple(paramz));
}
});
_android_output_path = new InputFile(Gtk.FileChooserAction.SELECT_FOLDER);
_android_config = make_deploy_config_combo();
_android_armv7 = new InputBool();
_android_armv7.value = false;
_android_armv8 = new InputBool();
_android_armv8.value = true;
_android_use_debug_keystore = new InputBool();
_android_use_debug_keystore.value_changed.connect(() => { android_set_debug_keystore(); });
_android_use_debug_keystore.value = true;
_android_keystore = new InputFile(Gtk.FileChooserAction.OPEN);
_android_keystore_password = new Gtk.Entry();
_android_keystore_password.set_visibility(false);
_android_keystore_password.input_purpose = Gtk.InputPurpose.PASSWORD;
_android_key_alias = new Gtk.Entry();
_android_key_password = new Gtk.Entry();
_android_key_password.set_visibility(false);
_android_key_password.input_purpose = Gtk.InputPurpose.PASSWORD;
_android_app_title = new InputString();
_android_app_title._entry.placeholder_text = "My Application";
_android_app_title.value = _project.name();
_android_app_identifier = new Gtk.Entry();
_android_app_identifier.placeholder_text = "org.company.product";
_android_app_version_code = new Gtk.Entry();
_android_app_version_code.input_purpose = Gtk.InputPurpose.DIGITS;
_android_app_version_code.placeholder_text = "1";
_android_app_version_name = new Gtk.Entry();
_android_app_version_name.placeholder_text = "1.0";
android_set_debug_keystore();
_android_set = new PropertyGridSet();
// Android Output grid.
PropertyGrid cv;
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Destination", _android_output_path);
cv.add_row("Config", _android_config);
cv.add_row("ARMv7-A", _android_armv7).sensitive = can_build_32bit_arm;
_android_armv7.sensitive = can_build_32bit_arm;
cv.add_row("ARMv8-A", _android_armv8);
_android_set.add_property_grid(cv, "Output");
// Android Application.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Title", _android_app_title);
cv.add_row("Identifier", _android_app_identifier);
cv.add_row("Version Code", _android_app_version_code);
cv.add_row("Version Name", _android_app_version_name);
_android_set.add_property_grid(cv, "Application");
// Android Signing.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Use debug keystore", _android_use_debug_keystore);
cv.add_row("Keystore", _android_keystore);
cv.add_row("Keystore password", _android_keystore_password);
cv.add_row("Alias", _android_key_alias);
cv.add_row("Key password", _android_key_password);
_android_set.add_property_grid(cv, "Signing");
// Android box.
_android_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
_android_box.pack_start(_android_deploy_button, false, true, 0);
_android_box.pack_start(_android_set, false, true, 0);
_android = new AndroidDeployer();
_android_page = new DeployerPage(TargetPlatform.ANDROID, _android_box, _android.check_config);
// HTML5 page.
_html5_deploy_button = make_deploy_button(TargetPlatform.HTML5);
_html5_deploy_button.clicked.connect(() => {
// Validate input fields.
string? output_path = _html5_output_path.value;
if (output_path == null) {
loge("Select a valid output Destination");
return;
}
string app_title = _html5_app_title.value.strip();
if (app_title.length == 0) {
loge("Enter a valid Title");
return;
}
// Create the package.
GLib.Variant paramz[] =
{
(string)output_path,
int.parse(_html5_config.value),
app_title
};
GLib.Application.get_default().activate_action("create-package-html5"
, new GLib.Variant.tuple(paramz));
});
_html5_output_path = new InputFile(Gtk.FileChooserAction.SELECT_FOLDER);
_html5_config = make_deploy_config_combo();
_html5_app_title = new InputString();
_html5_app_title._entry.placeholder_text = "My Application";
_html5_app_title.value = _project.name();
_html5_set = new PropertyGridSet();
// HTML5 box.
_html5_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
_html5_box.pack_start(_html5_deploy_button, false, true, 0);
_html5_box.pack_start(_html5_set, false, true, 0);
_html5 = new HTML5Deployer();
_html5_page = new DeployerPage(TargetPlatform.HTML5, _html5_box, _html5.check_config);
// HTML5 General page.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Destination", _html5_output_path);
cv.add_row("Config", _html5_config);
_html5_set.add_property_grid(cv, "Output");
// HTML5 Application.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Title", _html5_app_title);
_html5_set.add_property_grid(cv, "Application");
// Linux page.
_linux_deploy_button = make_deploy_button(TargetPlatform.LINUX);
_linux_deploy_button.clicked.connect(() => {
// Validate input fields.
string? output_path = _linux_output_path.value;
if (output_path == null) {
loge("Select a valid output Destination");
return;
}
string app_title = _linux_app_title.value.strip();
if (app_title.length == 0) {
loge("Enter a valid Title");
return;
}
// Create the package.
GLib.Variant paramz[] =
{
(string)output_path,
int.parse(_linux_config.value),
app_title
};
GLib.Application.get_default().activate_action("create-package-linux"
, new GLib.Variant.tuple(paramz));
});
_linux_output_path = new InputFile(Gtk.FileChooserAction.SELECT_FOLDER);
_linux_config = make_deploy_config_combo();
_linux_app_title = new InputString();
_linux_app_title._entry.placeholder_text = "My Application";
_linux_app_title.value = _project.name();
_linux_set = new PropertyGridSet();
// Linux box.
_linux_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
_linux_box.pack_start(_linux_deploy_button, false, true, 0);
_linux_box.pack_start(_linux_set, false, true, 0);
_linux_page = new DeployerPage(TargetPlatform.LINUX, _linux_box);
// Linux General page.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Destination", _linux_output_path);
cv.add_row("Config", _linux_config);
_linux_set.add_property_grid(cv, "Output");
// Linux Application.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Title", _linux_app_title);
_linux_set.add_property_grid(cv, "Application");
// Windows page.
_windows_deploy_button = make_deploy_button(TargetPlatform.WINDOWS);
_windows_deploy_button.clicked.connect(() => {
// Validate input fields.
string? output_path = _windows_output_path.value;
if (output_path == null) {
loge("Select a valid output Destination");
return;
}
string app_title = _windows_app_title.value.strip();
if (app_title.length == 0) {
loge("Enter a valid Title");
return;
}
// Create the package.
GLib.Variant paramz[] =
{
(string)output_path,
int.parse(_windows_config.value),
app_title
};
GLib.Application.get_default().activate_action("create-package-windows"
, new GLib.Variant.tuple(paramz));
});
_windows_output_path = new InputFile(Gtk.FileChooserAction.SELECT_FOLDER);
_windows_config = make_deploy_config_combo();
_windows_app_title = new InputString();
_windows_app_title._entry.placeholder_text = "My Application";
_windows_app_title.value = _project.name();
_windows_set = new PropertyGridSet();
// Windows box.
_windows_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
_windows_box.pack_start(_windows_deploy_button, false, true, 0);
_windows_box.pack_start(_windows_set, false, true, 0);
_windows_page = new DeployerPage(TargetPlatform.LINUX, _windows_box);
// Windows General page.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Destination", _windows_output_path);
cv.add_row("Config", _windows_config);
_windows_set.add_property_grid(cv, "Output");
// Windows Application.
cv = new PropertyGrid();
cv.column_homogeneous = true;
cv.add_row("Title", _windows_app_title);
_windows_set.add_property_grid(cv, "Application");
// Add pages.
_notebook = new Gtk.Notebook();
#if CROWN_PLATFORM_LINUX
_notebook.append_page(_linux_page, new Gtk.Label(TargetPlatform.LINUX.to_label()));
#elif CROWN_PLATFORM_WINDOWS
_notebook.append_page(_windows_page, new Gtk.Label(TargetPlatform.WINDOWS.to_label()));
#endif
_notebook.append_page(_android_page, new Gtk.Label(TargetPlatform.ANDROID.to_label()));
_notebook.append_page(_html5_page, new Gtk.Label(TargetPlatform.HTML5.to_label()));
_notebook.vexpand = true;
_notebook.show_border = false;
_controller_key = new Gtk.EventControllerKey(this);
_controller_key.key_pressed.connect(on_key_pressed);
this.add(_notebook);
}
public bool on_key_pressed(uint keyval, uint keycode, Gdk.ModifierType state)
{
if (keyval == Gdk.Key.Escape)
this.close();
return Gdk.EVENT_PROPAGATE;
}
public void android_set_debug_keystore()
{
bool sensitive = !_android_use_debug_keystore.value;
_android_keystore.sensitive = sensitive;
_android_keystore_password.sensitive = sensitive;
_android_key_alias.sensitive = sensitive;
_android_key_password.sensitive = sensitive;
}
}
} /* namespace Crown */