Commit caf15a4a authored by Hugo Neves de Carvalho's avatar Hugo Neves de Carvalho
Browse files

Add dialogs

parent 7cef9f4a
<!DOCTYPE html>
<html>
<head>
<link id="page_style" rel="stylesheet" type="text/css" href="css/default.css">
</head>
<body class="dialog_body">
<div class="container">
<form>
<fieldset id="data_container">
<legend id="legend"></legend>
</fieldset>
<div class="submit_buttons">
<button type="button" onClick="ok()" data-i18n="[title]iem.prop.ok_tt">
<span data-i18n="iem.prop.ok"></span>
</button>
<button type="button" onClick="cancel()" data-i18n="[title]iem.prop.cancel_tt">
<span data-i18n="iem.prop.cancel"></span>
</button>
</div>
</form>
</div>
<script>
"use strict";
if (typeof(pdbundle) !== "undefined"){
var pdgui = pdbundle.pdgui
}else{
var gui = require("nw.gui");
var pdgui = require("./pdgui.js");
}
// For translations
var l = pdgui.get_local_string;
pdgui.skin.apply(window);
var pd_object_callback;
var template_string;
var canvas;
function apply() {
var data_string = "",
scalar_inputs = document.querySelectorAll("input.scalar_value"),
vector_textarea = document.querySelector("textarea"),
i,
datum;
// start with the template header
data_string += template_string;
// add the data separator
data_string += "\n;\n;\n"
// now fetch the template name from the label as the first token for
// line with the scalar data
data_string += document.getElementById("legend").textContent;
for (i = 0; i < scalar_inputs.length; i++) {
data_string += " ";
if (scalar_inputs[i].title === "float") {
data_string += Number(scalar_inputs[i].value);
} else {
data_string += scalar_inputs[i].value;
}
}
// add terminating semicolon and newline for the scalar value line
data_string += ";\n";
// now tack on any vector data we may have
if (vector_textarea) {
data_string += vector_textarea.value;
// strip off the trailing semicolon. Otherwise Pd will crash...
data_string = data_string.slice(0, -1);
}
// trim the string... otherwise we could append an extra semicolon and
// crash Pd! So brittle...
data_string = data_string.trim();
data_string.split("\n").forEach(function (line) {
pdgui.pdsend(pd_object_callback, "data", line);
});
pdgui.pdsend(pd_object_callback, "end");
}
function cancel() {
//window.close(true);
pdgui.pdsend(pd_object_callback, "cancel");
if(pdgui.is_webapp()){
remove_dialog(pd_object_callback)
}
}
function ok() {
apply();
cancel();
}
function change_size() {
apply();
}
function parse_template_string(t_string) {
// slice off the leading "data;" line
t_string = t_string.slice(t_string.indexOf("\n") + 1);
var ret = [];
t_string.split("\n;\n").forEach(function (t) {
var t_object = {},
t_lines = t.split("\n");
// template name from "template foo;"
t_object.template = t_lines.shift().split(" ")[1].slice(0, -1);
t_object.fields = [];
t_lines.forEach(function (line) {
// remove trailing ";"
line = line.slice(0, -1);
var tokens = line.split(" "),
field = {};
if (tokens[0] === "float" || tokens[0] === "symbol") {
field["type"] = tokens[0];
field["var"] = tokens[1];
t_object.fields.push(field);
}
});
t_lines.forEach(function (line) {
// remove trailing ";"
line = line.slice(0, -1);
var tokens = line.split(" "),
field = {};
if (tokens[0] === "array" ||
tokens[0] === "canvas" ||
tokens[0] === "list") {
field["type"] = tokens[0];
field["var"] = tokens[1];
field["template"] = tokens[2];
t_object.fields.push(field);
}
});
ret.push(t_object);
});
return ret;
}
function add_break(container) {
var br = document.createElement("br");
br.style.setProperty("clear", "both");
container.appendChild(br);
}
function toggle_vector_editing() {
var state = document.getElementById("vector_edit_checkbox").checked;
document.getElementById("vector_textarea").disabled = state ? false : true;
}
function add_textarea_input(first_row) {
var label = document.createElement("label"),
textarea = document.createElement("textarea"),
check_label = document.createElement("label"),
check = document.createElement("input"),
outer_container = document.getElementById("data_container"),
inner_container = document.createElement("div"),
br = document.createElement("br");
label.textContent = "vector fields";
label.style.setProperty("display", "block");
label.style.setProperty("text-align", "left");
textarea.style.setProperty("display", "block");
textarea.setAttribute("id", "vector_textarea");
textarea.setAttribute("rows", "4");
textarea.setAttribute("col", "5");
textarea.style.setProperty("width", "11.3em");
textarea.disabled = true;
check.type = "checkbox";
check.id = "vector_edit_checkbox";
check.onclick = toggle_vector_editing;
check_label.appendChild(check);
check_label.appendChild(document.createTextNode("edit vector data"));
if (!first_row) {
add_break(outer_container);
}
inner_container.appendChild(label);
inner_container.appendChild(textarea);
outer_container.appendChild(inner_container);
outer_container.appendChild(check_label);
}
function add_text_input(field, left_column, first_row) {
var label = document.createElement("label"),
text_input = document.createElement("input"),
outer_container = document.getElementById("data_container"),
inner_container = document.createElement("div"),
br;
inner_container.style.setProperty("float", "left");
// For floats, we do two inputs per line with a right margin for
// the left column. For symbols, the input takes up the whole line
if (left_column && field.type !== "symbol") {
inner_container.style.setProperty("margin-right", "1em");
}
label.textContent = field["var"];
text_input.type = "text";
text_input.classList.add("scalar_value");
text_input.title = field["type"];
// Set styles-- should be done in css, but quick-and-dirty for now
label.style.setProperty("display", "block");
// This is in opposition to iemgui dialog. But the iemgui dialog should
// be changed to have labels at the top of the input. It makes everything
// line up better...
label.style.setProperty("text-align", "left");
text_input.style.setProperty("display", "block");
// Also in opposition to iemgui dialogs
text_input.style.setProperty("width",
field.type === "float" ? "5em" : "11.3em");
// Some bottom margin
inner_container.style.setProperty("margin-bottom", "8px");
// Break before left column or symbol input, except for the first row
if (!first_row) {
if (field.type === "symbol" || left_column) {
add_break(outer_container);
}
}
inner_container.appendChild(label);
inner_container.appendChild(text_input);
outer_container.appendChild(inner_container);
}
function build_form(template_string) {
var t_array = parse_template_string(template_string),
t,
i,
j,
left_column;
// For now we just build the form from the main template
// for the scalar. If there are any array, canvas, or list
// fields we just put all of their contents in a multi-line text
// widget.
t = t_array[0];
document.getElementById("legend").textContent = t.template;
left_column = false;
for (i = 0; i < t.fields.length; i++) {
if (t.fields[i].type === "symbol" ||
t.fields[i].type === "float") {
if (t.fields[i].type === "float") {
left_column = !left_column;
} else {
left_column = true;
}
add_text_input(t.fields[i], left_column, i === 0);
if (t.fields[i].type === "symbol") {
left_column = false;
}
}
}
// Now for array, canvas, and text fields. These get a single textarea
// for now because it's non-trivial to parse (much less workably display)
// nested arrays
for (j = 0; j < t.fields.length; j++) {
if (t.fields[j].type === "array" ||
t.fields[j].type === "canvas" ||
t.fields[j].type === "list") {
// Add a textarea, but only prepend a break if we already have
// text inputs in the form
add_textarea_input(j == 0 && i !== 0);
break;
}
}
}
function parse_data_string(d_string) {
// trim off leading/trailing spaces and newlines, then split on newlines
var lines = d_string.trim().split("\n"),
scalar_values = lines[0].slice(0, -1).split(" "),
// for now we're not trying to parse the vector data. Nested arrays
// make it non-trivial to parse this
vector_values = lines.slice(1).join("\n"),
ret_obj = {};
ret_obj.name = scalar_values.shift();
ret_obj.scalar = scalar_values;
ret_obj.vector_string = vector_values;
return ret_obj;
}
function populate_form(data_string) {
var d_array = parse_data_string(data_string),
inputs = document.querySelectorAll("input.scalar_value"),
textareas = document.querySelectorAll("textarea"),
i;
for (i = 0; i < inputs.length; i++) {
// skip the leading template name
inputs[i].value = d_array.scalar[i].toString();
}
// For now, we throw all the vector fields into a single textarea
if (textareas.length) {
textareas[0].textContent = d_array.vector_string;
}
}
// This gets called from the nw_create_window function in index.html
// It provides us with our window id from the C side. Once we have it
// we can create the menu and register event callbacks
function register_window_id(gfxstub, data_string) {
var head, tail, templates, data, data_separator;
pd_object_callback = gfxstub;
add_events(gfxstub);
// slice off the head of the message. This is where the templates
// for the data-- plus any templates for (nested) arrays-- are kept.
// We will keep the head intact for sending back to Pd since the user
// won't be able to change any of that data.
data_separator = "\n;\n;";
head = data_string.slice(0, data_string.indexOf(data_separator));
// Note: we need to keep a copy of the incoming data_string so that
// the user can revert if need be.
tail = data_string.slice(data_string.indexOf(data_separator) +
data_separator.length);
//pdgui.post("head of data is " + head);
template_string = head;
translate_form();
build_form(head); // Create form elements from the data template
populate_form(tail); // Fill the form we created with the actual data
// We don't turn on rendering of the "container" div until
// We've finished displaying all the spans and populating the
// labels and form elements. That makes it more efficient and
// snappier, at least on older machines.
document.getElementsByClassName("container")[0]
.style.setProperty("display", "inline");
}
window.register_dialog = register_window_id;
// Stop-gap translator
function translate_form() {
var elements = document.querySelectorAll("[data-i18n]"),
data,
i;
for (i = 0; i < elements.length; i++) {
data = elements[i].dataset.i18n;
if (data.slice(0, 7) === "[title]") {
elements[i].title = l(data.slice(7));
} else {
elements[i].textContent = l(data);
}
}
}
function get_attr(name, attrs) {
return attrs[attrs.indexOf(name) + 1];
}
function get_elem(name) {
return document.getElementById(name);
}
function add_events(name) {
// closing the Window
if(!pdgui.is_webapp()){
gui.Window.get().on("close", function () {
// this needs to do whatever the "cancel" button does
cancel();
});
}
pdgui.dialog_bindings(name);
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link id="page_style" rel="stylesheet" type="text/css" href="css/default.css">
</head>
<body class="dialog_body">
<div class="container">
<form>
<fieldset>
<legend></legend>
</fieldset>
<div class="submit_buttons">
<button type="button"
onClick="ok();"
id="ok_button"
data-i18n="[title]iem.prop.ok_tt">
<span data-i18n="iem.prop.ok"></span>
</button>
<button type="button"
onClick="apply();"
id="apply_button"
data-i18n="[title]iem.prop.apply_tt">
<span data-i18n="iem.prop.apply"></span>
</button>
<button type="button"
onClick="cancel(true);"
data-i18n="[title]iem.prop.cancel_tt">
<span data-i18n="iem.prop.cancel"></span>
</button>
</div>
</form>
</div>
<script>
"use strict";
if (typeof(pdbundle) !== "undefined"){
var pdgui = pdbundle.pdgui
}else{
var gui = require("nw.gui");
var pdgui = require("./pdgui.js");
}
// For translations
var l = pdgui.get_local_string;
pdgui.skin.apply(window);
var pd_object_callback;
var old_properties; // Original values in case the user wants to cancel changes
var new_properties; // Updated values
// Grab focus for one of the buttons
function focus_button(id) {
document.getElementById(id).focus();
}
function send_props_to_pd(attr_array) {
var values = [];
// Todo: parse symbols the same way we do for iemguis before
// sending them back to Pd. This might require some fanagling on
// the pd side of things...
attr_array.forEach(function(elem) {
values.push(elem.value);
});
pdgui.pdsend(pd_object_callback, "dialog", values.join(" "));
}
function apply() {
focus_button("apply_button");
send_props_to_pd(new_properties);
}
function cancel() {
send_props_to_pd(old_properties);
pdgui.pdsend(pd_object_callback, "cancel");
if(pdgui.is_webapp()){
remove_dialog(pd_object_callback)
}
}
function ok() {
// Focus button so that the "change" event triggers
// for the form before sending values to Pd
focus_button("ok_button");
send_props_to_pd(new_properties);
pdgui.pdsend(pd_object_callback, "cancel");
if(pdgui.is_webapp()){
remove_dialog(pd_object_callback)
}
}
// turn a ["name", value, etc.] array
// into an array of objects where each object contains
// name: name for the input element
// type: type of element (number, string, toggle)
// label: string label (basically the name with underscores turned to spaces)
// value: a value for the input
function parse_attrs(attrs) {
var ret = [],
elem;
attrs.forEach(function(token, i) {
if (i % 2 === 0) {
elem = {};
token = token.split("_");
if (token.length > 1) {
elem.type = token[token.length - 1];
if (elem.type !== "symbol" &&
elem.type !== "toggle") {
// no suffix defaults to "number"
elem.type = "number";
} else {
// remove the type suffix
token = token.slice(0, -1);
}
} else {
elem.type = "number";
}
elem.name = token.join("_");
elem.label = token.join(" ");
} else {
elem.value = token;
// now push the object onto the array
ret.push(elem);
}
});
return ret;
}
// This gets called from the nw_create_window function in index.html
// It provides us with our window id from the C side. Once we have it
// we can create the menu and register event callbacks
function register_window_id(gfxstub, args) {
var external_name = args.name,
array_of_objects;
pd_object_callback = gfxstub;
array_of_objects = parse_attrs(args.attributes);
// Store our array for later use
new_properties = array_of_objects;
// Also make a copy in the case that the user cancels this dialog
old_properties = JSON.parse(JSON.stringify(array_of_objects));
add_events(gfxstub);
// not sure that we need this for properties windows
//pdgui.canvas_map(gfxstub);
translate_form();
build_form(external_name, array_of_objects);
// We don't turn on rendering of the "container" div until
// We've finished displaying all the spans and populating the
// labels and form elements. That makes it more efficient and
// snappier, at least on older machines.
document.getElementsByClassName("container")[0]
.style.setProperty("display", "inline");
}
window.register_dialog = register_window_id;
// Stop-gap translator
function translate_form() {
var elements = document.querySelectorAll("[data-i18n]"),
data,
i;
for (i = 0; i < elements.length; i++) {
data = elements[i].dataset.i18n;
if (data.slice(0, 7) === "[title]") {
elements[i].title = l(data.slice(7));
} else {
elements[i].textContent = l(data);
}
}
}
function get_input_type(t) {
return t === "symbol" ? "text" :
t === "number" ? "text" :
t === "toggle" ? "checkbox":
"text";
}
function build_form(external_name, array_of_objects) {
var fieldset = document.querySelector("fieldset");
document.querySelector("legend").textContent = external_name;
array_of_objects.forEach(function(elem) {
var input_elem = document.createElement("input"),
label = document.createElement("label");
input_elem.type = get_input_type(elem.type);
if (input_elem.type === "checkbox") {
input_elem.checked = elem.value !== 0;
input_elem.onchange = function() {
elem.value = input_elem.checked ? 1 : 0;
};
} else {
input_elem.value = elem.value;
input_elem.onchange = function() {
elem.value = input_elem.value;