diff --git a/pd/nw/css/c64.css b/pd/nw/css/c64.css index bca01f1a8d1c0cc396b173f22c0884c8eb50f62e..086afaba2caa5a05ed26f8af606cba74d643f966 100644 --- a/pd/nw/css/c64.css +++ b/pd/nw/css/c64.css @@ -220,6 +220,50 @@ text { fill: #3e32a2; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. For boxes that + output the value we don't need a CSS rule, as the arrow will be filled + black by default */ +.atom .index_arrow { + stroke: black; + stroke-width: 1; + fill: none; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: #7569d7; + cursor: pointer; + box-shadow: 2px 2px 0px #7569d7; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: #3e32a2; + outline: #7569d7; +} + +#dropdown_list li { + color: #a49aea; + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + color: black; + background: #e87216; +} + .obj .border { fill: #3e32a2; stroke: #7569d7; diff --git a/pd/nw/css/default.css b/pd/nw/css/default.css index f0ed0040221c6061ececac8667173cdbb79cf880..21271136713a8a18af7ccd39f86c933094c50b5e 100644 --- a/pd/nw/css/default.css +++ b/pd/nw/css/default.css @@ -334,6 +334,47 @@ text { fill: #eee; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. For boxes that + output the value we don't need a CSS rule, as the arrow will be filled + black by default */ +.atom .index_arrow { + stroke: black; + stroke-width: 1; + fill: none; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: #c3c3c3; + cursor: pointer; + box-shadow: 5px 0 5px -5px #aaa, 0 5px 5px -5px #aaa, -5px 0 5px -5px #aaa; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: #eee; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background-color: #c3c3c3; +} + .obj .border { fill: #f6f8f8; stroke: #ccc; @@ -410,11 +451,6 @@ text { fill: blue; } -/* gatom "activated" text (i.e., when it has the keyboard focus) */ -.atom.activated text { - fill: red; -} - /* test of xlet hover animation... this should probably use the web animation API instead. That way the animation won't get cut off when you diff --git a/pd/nw/css/extended.css b/pd/nw/css/extended.css index 2e320b165087c23648efe8fc52254557962cbb7e..53e0728fbc5facfd5b6b06f07de0be9a1825d379 100644 --- a/pd/nw/css/extended.css +++ b/pd/nw/css/extended.css @@ -214,6 +214,47 @@ text { fill: #e0e0e0; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. For boxes that + output the value we don't need a CSS rule, as the arrow will be filled + black by default */ +.atom .index_arrow { + stroke: black; + stroke-width: 1; + fill: none; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: #c3c3c3; + cursor: pointer; + box-shadow: 5px 0 5px -5px #888, 0 5px 5px -5px #888, -5px 0 5px -5px #888; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: #eee; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background: #c3c3c3; +} + .obj .border { fill: #f6f8f8; stroke: #c1c1c1; diff --git a/pd/nw/css/inverted.css b/pd/nw/css/inverted.css index 76a42c44db381461714e6658cff1be79ae0442c6..009384432b76bd783f2b9ad9cd2786e7847c46e3 100644 --- a/pd/nw/css/inverted.css +++ b/pd/nw/css/inverted.css @@ -232,6 +232,50 @@ text { fill: #111; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. */ +.atom .index_arrow { + stroke: #a294a2; + stroke-width: 1; + fill: none; +} + +.atom .value_arrow { + fill: #a294a2; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: #999; + cursor: pointer; + color: white; + box-shadow: 5px 0 5px -5px #999, 0 5px 5px -5px #999, -5px 0 5px -5px #999; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: black; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background: #555; +} + .obj .border { fill: #090707; stroke: #3e3e3e; diff --git a/pd/nw/css/strongbad.css b/pd/nw/css/strongbad.css index c7e00cc25fddf70f886b6886f35591f307b10a24..fb26b42c62fd56a1310b271632c4f5db605737f3 100644 --- a/pd/nw/css/strongbad.css +++ b/pd/nw/css/strongbad.css @@ -222,6 +222,51 @@ text { fill: black; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. */ +.atom .index_arrow { + stroke: #4bd046; + stroke-width: 1; + fill: none; +} + +.atom .value_arrow { + fill: #4bd046; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: #0b560b; + cursor: pointer; + color: #4bd046; + box-shadow: 1px 1px 1px 1px #0b560b; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: black; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background: #4bd046; + color: black; +} + .obj .border { fill: black; stroke: #0b560b; diff --git a/pd/nw/css/subdued.css b/pd/nw/css/subdued.css index f4966126425eb64da9a0e6c618007275c6d5d924..fd9d33413bfae848e770dd7cdb14a442d988b40b 100644 --- a/pd/nw/css/subdued.css +++ b/pd/nw/css/subdued.css @@ -221,6 +221,46 @@ text { fill: #9fc79f; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. For boxes that + output the value we don't need a CSS rule, as the arrow will be filled + black by default */ +.atom .index_arrow { + stroke: black; + stroke-width: 1; + fill: none; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: #b1d3b1; + cursor: pointer; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: #9fc79f; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background: #c3c3c3; +} + .obj .border { fill: #c0dcc0; stroke: #666666; diff --git a/pd/nw/css/vanilla.css b/pd/nw/css/vanilla.css index 1ac5e5792f9d0bf7e8dcc6a40e2f90470bedbc3b..2694e502c96a7ba84421fcf769004c1947b89a67 100644 --- a/pd/nw/css/vanilla.css +++ b/pd/nw/css/vanilla.css @@ -215,6 +215,46 @@ text { fill: none; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. For boxes that + output the value we don't need a CSS rule, as the arrow will be filled + black by default */ +.atom .index_arrow { + stroke: black; + stroke-width: 1; + fill: none; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: black; + cursor: pointer; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: white; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background: #c3c3c3; +} + .obj .border { fill: none; stroke: black; diff --git a/pd/nw/css/vanilla_inverted.css b/pd/nw/css/vanilla_inverted.css index e1084e19148b1eb8961b319622b0eaca608eee61..aae6f878f51cd3fcb5f0147bb2ed7cdf17194722 100644 --- a/pd/nw/css/vanilla_inverted.css +++ b/pd/nw/css/vanilla_inverted.css @@ -226,6 +226,52 @@ text { fill: none; } +/* for dropdown box we want to visually distinguish boxes that output + the index from boxes that output the value. For now we do that by + stroking the arrow for boxes that output an index. For boxes that + output the value we don't need a CSS rule, as the arrow will be filled + black by default */ +.atom .index_arrow { + stroke: white; + stroke-width: 1; + fill: none; +} + +.atom .value_arrow { + fill: white; +} + +/* gatom "activated" text (i.e., when it has the keyboard focus) */ +.atom.activated text { + fill: red; +} + +#dropdown_list { + position: absolute; + border-width: 1px; + border-style: solid; + border-color: white; + cursor: pointer; + color: white; +} + +#dropdown_list ol { + list-style-position: inside; + margin: 0; + padding: 0; + background: black; +} + +#dropdown_list li { + list-style-type: none; + padding: 5px; +} + +#dropdown_list li.highlighted { + background: #c3c3c3; + color: black; +} + .obj .border { fill: none; stroke: white; diff --git a/pd/nw/dialog_gatom.html b/pd/nw/dialog_gatom.html index 5a6be0ca11ec7fcc4ae945ffc30a3c57b57cc5f3..5d0e9fba4c197f45f6fd282d1e4ae39fb2a54e2c 100644 --- a/pd/nw/dialog_gatom.html +++ b/pd/nw/dialog_gatom.html @@ -7,7 +7,7 @@ <div class="container"> <form> <fieldset> - <legend data-i18n="gatom.prop.gatom"></legend> + <legend id="dialog_header"></legend> <table class="pairs"> <tr class="width prop"> @@ -45,6 +45,22 @@ onchange="update_attr(this)"> </td> </tr> + + <tr class="outtype prop"> + <td> + <label data-i18n="[title]gatom.prop.dropdown_outtype_tt"> + <span data-i18n="gatom.prop.dropdown_outtype"></span> + </label> + </td> + <td colspan="3" + data-i18n="[title]gatom.prop.dropdown_outtype_tt"> + <select id="outtype_select" + onchange="update_dropdown_outtype(this);"> + <option>index</option> + <option>value</option> + </select> + </td> + </tr> <tr> <td> <label data-i18n="[title]iem.prop.send_tt"> @@ -215,12 +231,17 @@ function update_attr(elem) { new_attrs[elem.name] = elem.value; } +function update_dropdown_outtype(elem) { + new_attrs.outtype = elem.selectedIndex; +} + function send_params(attrs, create_undo_point) { //pdgui.post("we're applying gatom changes!"); + var gatom = attrs.name === "atom"; pdgui.pdsend(pd_object_callback, "param", +attrs.width, - +attrs.draglo, - +attrs.draghi, + gatom ? +attrs.draglo : +attrs.outtype, + gatom ? +attrs.draghi : 0, gatom_escape(attrs.label), +attrs.labelpos, gatom_escape(attrs.receive_symbol), @@ -266,9 +287,20 @@ function register_window_id(gfxstub, attributes) { var attr; pd_object_callback = gfxstub; add_events(gfxstub); + + // before translating, set the header based on class name: + document.querySelector("#dialog_header") + .setAttribute("data-i18n", "gatom.prop." + + (attributes.name === "dropdown" ? "dropdown" : "gatom")); + translate_form(); populate_form(attributes); + // hide outtype select for "dropdown", or draglo/hi for "gatom" + document.querySelector(attributes.name === "atom" ? ".outtype" : ".draglo") + .style.setProperty("display", "none"); + + // Hack... change incoming "-" to empty string if (attributes.label === "-") { attributes.label = ""; } if (attributes.send_symbol === "-") { attributes.send_symbol= ""; } @@ -280,6 +312,7 @@ function register_window_id(gfxstub, attributes) { new_attrs[attr] = attributes[attr]; } } + old_attrs = attributes; // We don't turn on rendering of the "container" div until // We've finished displaying all the spans and populating the @@ -320,8 +353,12 @@ function get_elem(name) { function populate_form(attributes) { var label, snd, rcv, labelpos, i, radios; get_elem("width").value = attributes.width; - get_elem("draglo").value = attributes.draglo; - get_elem("draghi").value = attributes.draghi; + if (attributes.name === "atom") { + get_elem("draglo").value = attributes.draglo; + get_elem("draghi").value = attributes.draghi; + } else { + get_elem("outtype_select").selectedIndex = attributes.outtype; + } label = attributes.label; get_elem("label").value = gatom_unescape(label); snd = attributes.send_symbol; diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json index fa694938bcd3a9c6c8ce99b525683999c0a56071..8b51347374622bcb41d0cea9d289fb53bc47c7e2 100644 --- a/pd/nw/locales/de/translation.json +++ b/pd/nw/locales/de/translation.json @@ -79,6 +79,7 @@ "gatom": { "prop": { "gatom": "Atom-Box", + "dropdown": "Ausgezähltwunderbox", "label": "Etikett", "label_left": "links", "label_right": "rechts", @@ -89,6 +90,8 @@ "label_bottom": "unten", "label_left": "links", "label_right": "rechts", + "dropdown_outtype": "output", + "dropdown_outtype_tt": "whether to output the index or the value", "width": "Breite", "width_tt": "Breite (in Zeichen)" } @@ -195,6 +198,8 @@ "symbol_tt": "Füge dem Patch ein Feld zur Eingabe und Anzeige von Symbolen hinzu", "comment": "Kommentar", "comment_tt": "Füge dem Patch einen Kommentar hinzu", + "dropdown": "Dropdown", + "dropdown_tt": "Dropdown menu", "bang": "Bang", "bang_tt": "Füge dem Patch einen Taster zum Senden von Bang-Nachrichten hinzu", "toggle": "Toggle", diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json index 8e83ee1d7ead3b9337cb560bd74d85cc2333129c..44d4e12e5b3ebe575c136f044026bf0f56ca708e 100644 --- a/pd/nw/locales/en/translation.json +++ b/pd/nw/locales/en/translation.json @@ -79,6 +79,7 @@ "gatom": { "prop": { "gatom": "atom box", + "dropdown": "dropdown menu", "label": "label", "label_left": "left", "label_right": "right", @@ -89,6 +90,8 @@ "label_bottom": "bottom", "label_left": "left", "label_right": "right", + "dropdown_outtype": "output", + "dropdown_outtype_tt": "whether to output the index or the value", "width": "width", "width_tt": "width (in characters)" } @@ -195,6 +198,8 @@ "symbol_tt": "Add a box to type and display a symbol on the canvas", "comment": "Comment", "comment_tt": "Write a comment on the canvas", + "dropdown": "Dropdown", + "dropdown_tt": "Dropdown menu", "bang": "Bang", "bang_tt": "Add a graphical button to the canvas for sending bang messages", "toggle": "Toggle", diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html index 78460d810e6619ebcb819b64dcaac35d71252c3f..3d3d4d801adeec9efedc125850e9da4c20696a1e 100644 --- a/pd/nw/pd_canvas.html +++ b/pd/nw/pd_canvas.html @@ -47,6 +47,9 @@ <span data-i18n="canvas.find.search"></span> </button> </div> + <div style="display:none;" id="dropdown_list" class="noselect"> + <ol></ol> + </div> <dialog id="save_before_quit"> <h4><span data-i18n="canvas.save_dialog.prompt"></span> <span id="save_before_quit_filename"></span>? diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js index 8c066b8ad374a30b78f62bc3f003fd1adf91e4b7..106d56d5354de59bebe59475472e1e659b16cd02 100644 --- a/pd/nw/pd_canvas.js +++ b/pd/nw/pd_canvas.js @@ -197,6 +197,10 @@ var canvas_events = (function() { svg.setAttribute("width", w); svg.setAttribute("height", h); }, + dropdown_index_to_pd = function(elem) { + pdgui.pdsend(elem.getAttribute("data-callback"), + elem.querySelector(".highlighted").getAttribute("data-index")); + }, events = { mousemove: function(evt) { //pdgui.post("x: " + evt.pageX + " y: " + evt.pageY + @@ -460,6 +464,36 @@ var canvas_events = (function() { // Set last state (none doesn't count as a state) //pdgui.post("previous state is " + canvas_events.get_previous_state()); canvas_events[canvas_events.get_previous_state()](); + }, + dropdown_menu_mousedown: function(evt) { + var select_elem = document.querySelector("#dropdown_list"); + dropdown_index_to_pd(select_elem); + select_elem.style.setProperty("display", "none"); + canvas_events.normal(); + }, + dropdown_menu_mouseup: function(evt) { + var i, select_elem; + if (evt.target.parentNode + && evt.target.parentNode.parentNode + && evt.target.parentNode.parentNode.id === "dropdown_list") { + select_elem = document.querySelector("#dropdown_list"); + dropdown_index_to_pd(select_elem); + select_elem.style.setProperty("display", "none"); + canvas_events.normal(); + } + }, + dropdown_menu_mouseover: function(evt) { + var li_array; + if (evt.target.parentNode + && evt.target.parentNode.parentNode + && evt.target.parentNode.parentNode.id === "dropdown_list") { + li_array = evt.target.parentNode.querySelectorAll('li'); + li_array.forEach(function(e) { + e.classList.remove("highlighted"); + }); + evt.target.classList.add("highlighted"); + } + // hide the dropdown menu <div> thingy } }, utils = { @@ -701,6 +735,12 @@ var canvas_events = (function() { state = "floating_text"; set_edit_menu_modals(false); }, + dropdown_menu: function() { + this.none(); + document.addEventListener("mousedown", events.dropdown_menu_mousedown, false); + document.addEventListener("mouseup", events.dropdown_menu_mouseup, false); + document.addEventListener("mouseover", events.dropdown_menu_mouseover, false); + }, search: function() { this.none(); document.addEventListener("keydown", events.find_keydown, false); @@ -791,7 +831,7 @@ function register_window_id(cid, attr_array) { // Initialize the zoom level to the value retrieved from the patch, if any. nw.Window.get().zoomLevel = attr_array.zoom; pdgui.canvas_map(cid); // side-effect: triggers gui_canvas_get_scroll - set_editmode_checkbox(attr_array.editmode !== 0 ? true : false); + pdgui.canvas_set_editmode(cid, attr_array.editmod); // For now, there is no way for the cord inspector to be turned on by // default. But if this changes we need to set its menu item checkbox // accordingly here @@ -1319,6 +1359,14 @@ function nw_create_patch_window_menus(gui, w, name) { pdgui.pdsend(name, "text 0"); } }); + minit(m.put.dropdown, { + enabled: true, + click: function() { + update_live_box(); + pdgui.pdsend(name, "dirty 1"); + pdgui.pdsend(name, "dropdown 0"); + } + }); minit(m.put.bang, { enabled: true, click: function(e) { diff --git a/pd/nw/pd_menus.js b/pd/nw/pd_menus.js index 85d8fce67d0dcd6f955293a1d81d2662da0abdca..abe2935ac313cab1b36a4b6e0dce165e6b1d3c13 100644 --- a/pd/nw/pd_menus.js +++ b/pd/nw/pd_menus.js @@ -425,6 +425,12 @@ function create_menu(gui, type) { modifiers: cmd_or_ctrl, tooltip: l("menu.comment_tt") })); + put_menu.append(m.put.dropdown = new gui.MenuItem({ + label: l("menu.dropdown"), + //key: "6", + //modifiers: cmd_or_ctrl, + tooltip: l("menu.dropdown_tt") + })); put_menu.append(new gui.MenuItem({ type: "separator" })); put_menu.append(m.put.bang = new gui.MenuItem({ label: l("menu.bang"), diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index 82643aec74b9aef7ce5f02b036ffe5e26d0a5b8f..002843fb915261382ee521fe10bf2f2e8336ff21 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -951,8 +951,20 @@ function menu_send(name) { } // requires nw.js API (Menuitem) -function gui_canvas_set_editmode(cid, state) { +function canvas_set_editmode(cid, state) { + var patchsvg = patchwin[cid].window.document.querySelector("#patchsvg"); patchwin[cid].window.set_editmode_checkbox(state !== 0 ? true : false); + if (state !== 0) { + patchsvg.classList.add("editmode"); + } else { + patchsvg.classList.remove("editmode"); + } +} + +exports.canvas_set_editmode = canvas_set_editmode; + +function gui_canvas_set_editmode(cid, state) { + canvas_set_editmode(cid, state); } // requires nw.js API (Menuitem) @@ -1946,21 +1958,34 @@ function gui_message_redraw_border(cid, tag, width, height) { }); } -function atom_border_points(width, height) { - return [0, 0, +function atom_border_points(width, height, is_dropdown) { + // For atom, angle the top-right corner. + // For dropdown, angle both top-right and bottom-right corners + var bottom_right_x = is_dropdown ? width - 4 : width; + return [0, 0, width - 4, 0, width, 4, - width, height, + width, height - 4, + bottom_right_x, height, 0, height, 0, 0] .join(" "); } -function gui_atom_draw_border(cid, tag, width, height) { +function atom_arrow_points(width, height) { + var m = height < 20 ? 1 : height / 12; + return [width - (9 * m), height * 0.5 - Math.floor(1 * m), + width - (3 * m), height * 0.5 - Math.floor(1 * m), + width - (6 * m), height * 0.5 + Math.floor(4 * m), + ].join(" "); +} + + +function gui_atom_draw_border(cid, tag, type, width, height) { var g = get_gobj(cid, tag), - polygon; + polygon, arrow, m; polygon = create_item(cid, "polygon", { - points: atom_border_points(width, height), + points: atom_border_points(width, height, type !== 0), fill: "none", stroke: "gray", "stroke-width": 1, @@ -1968,11 +1993,23 @@ function gui_atom_draw_border(cid, tag, width, height) { //id: tag + "border" }); g.appendChild(polygon); + if (type !== 0) { // dropdown + // 1 = output index + // 2 = output value + // Let's make the two visually distinct so that the user can still + // reason about the patch functionality merely by reading the diagram + m = height < 20 ? 1 : height / 12; + arrow = create_item(cid, "polygon", { + points: atom_arrow_points(width, height), + "class": type === 1 ? "index_arrow" : "value_arrow" + }); + g.appendChild(arrow); + } } -function gui_atom_redraw_border(cid, tag, width, height) { +function gui_atom_redraw_border(cid, tag, is_dropdown, width, height) { var g = get_gobj(cid, tag), - p; + p, a; // Unfortunately Pd will send updates for gui objects that // lie outside the bounding box of a graph-on-parent subpach. // We should refrain from sending such messages from Pd, but for @@ -1986,6 +2023,12 @@ function gui_atom_redraw_border(cid, tag, width, height) { configure_item(p, { points: atom_border_points(width, height) }); + if (!!is_dropdown) { + a = g.querySelectorAll("polygon")[1]; + configure_item(a , { + points: atom_arrow_points(width, height) + }); + } } } } @@ -2305,6 +2348,14 @@ function elem_displace(elem, dx, dy) { t.matrix.f += dy; } +function elem_get_coords(elem) { + var t = elem.transform.baseVal.getItem(0); + return { + x: t.matrix.e, + y: t.matrix.f + } +} + // used for tidy up function gui_text_displace(name, tag, dx, dy) { elem_displace(get_gobj(name, tag), dx, dy); @@ -4383,6 +4434,61 @@ function gui_gatom_activate(cid, tag, state) { } } +function gui_dropdown_dialog(did, attr_array) { + // Just reuse the "gatom" dialog + dialogwin[did] = create_window(did, "gatom", 265, 300, + popup_coords[2], popup_coords[3], + attr_array_to_object(attr_array)); +} + +function dropdown_populate(cid, label_array, current_index) { + var ol = patchwin[cid] + .window.document.querySelector("#dropdown_list ol"); + // clear it out + ol.innerHTML = ''; + label_array.forEach(function(text, i) { + var li = patchwin[cid].window.document.createElement("li"); + li.textContent = text; + li.setAttribute("data-index", i); + if (i === current_index) { + li.classList.add("highlighted"); + } + ol.appendChild(li); + }); +} + +function gui_dropdown_activate(cid, obj_tag, tag, current_index, font_size, state, label_array) { + var g, select_elem, svg_view; + // Annoying: obj_tag is just the "x"-prepended hex value for the object, + // and tag is the one from rtext_gettag that is used as our gobj id + if (patchwin[cid]) { + g = get_gobj(cid, tag); + if (state !== 0) { + svg_view = patchwin[cid].window.document.getElementById("patchsvg") + .viewBox.baseVal; + dropdown_populate(cid, label_array, current_index); + select_elem = patchwin[cid] + .window.document.querySelector("#dropdown_list"); + // stick the obj_tag in a data field + select_elem.setAttribute("data-callback", obj_tag); + select_elem.style.setProperty("display", "inline"); + select_elem.style.setProperty("left", + (elem_get_coords(g).x - svg_view.x) + "px"); + select_elem.style.setProperty("top", + (elem_get_coords(g).y + g.getBBox().height - svg_view.y) + + "px"); + select_elem.style.setProperty("font-size", + pd_fontsize_to_gui_fontsize(font_size) + "px"); + select_elem.style.setProperty("min-width", g.getBBox().width + "px"); + patchwin[cid].window.canvas_events.dropdown_menu(); + } else { + post("deactivating dropdown menu"); + // Probably want to send this + pdsend(cid, "key 0 Control 0 1 0"); + } + } +} + function gui_iemgui_dialog(did, attr_array) { //for (var i = 0; i < attr_array.length; i++) { // attr_array[i] = '"' + attr_array[i] + '"'; diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c index aec7efd3ed82efb42e11ad296b53ed457bbd92a1..c0b10036b7baf97e43a5b940132a9764e778c246 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -2465,6 +2465,7 @@ extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_dropdown(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv); void g_graph_setup(void); @@ -2499,6 +2500,8 @@ void g_canvas_setup(void) gensym("floatatom"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_symbolatom, gensym("symbolatom"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_dropdown, + gensym("dropdown"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)glist_text, gensym("text"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"), diff --git a/pd/src/g_text.c b/pd/src/g_text.c index 65af6bed58c65e77e13943973f278300c47fbde4..d04d0fd94134033b1269cea2e8880ca262773add 100644 --- a/pd/src/g_text.c +++ b/pd/src/g_text.c @@ -22,6 +22,7 @@ t_class *text_class; t_class *message_class; static t_class *gatom_class; +static t_class *dropdown_class; static void text_vis(t_gobj *z, t_glist *glist, int vis); static void text_displace(t_gobj *z, t_glist *glist, int dx, int dy); @@ -1211,7 +1212,6 @@ static void gatom_getwherelabel(t_gatom *x, t_glist *glist, int *xp, int *yp) } } - static void gatom_displace(t_gobj *z, t_glist *glist, int dx, int dy) { @@ -1248,8 +1248,6 @@ static void gatom_vis(t_gobj *z, t_glist *glist, int vis) canvas_realizedollar(x->a_glist, x->a_label)->s_name, sys_hostfontsize(glist_getfont(glist)) ); - - } else { @@ -1351,7 +1349,7 @@ void canvas_atom(t_glist *gl, t_atomtype type, (void *)canvas_undo_set_create(glist_getcanvas(gl))); } glob_preset_node_list_seek_hub(); - glob_preset_node_list_check_loc_and_update(); + glob_preset_node_list_check_loc_and_update(); } void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) @@ -1385,6 +1383,7 @@ static void gatom_properties(t_gobj *z, t_glist *owner) gui_start_vmess("gui_gatom_dialog", "s", gfxstub_new2(&x->a_text.te_pd, x)); gui_start_array(); + gui_s("name"); gui_s("atom"); gui_s("width"); gui_i(x->a_text.te_width); gui_s("draglo"); gui_f(x->a_draglo); gui_s("draghi"); gui_f(x->a_draghi); @@ -1396,6 +1395,444 @@ static void gatom_properties(t_gobj *z, t_glist *owner) gui_end_vmess(); } +/* ---------------------- the "dropdown" text item ------------------------ */ + +typedef struct _dropdown +{ + t_text a_text; + t_binbuf *a_names; /* names to be displayed */ + int a_maxnamewidth; /* when width = 0 this is used */ + int a_index; /* index */ + t_glist *a_glist; /* owning glist */ + t_float a_dummy; /* dummy value */ + int a_outtype; /* 0 = index, 1 = value */ + int a_output; /* 0 = index, 1 = value */ + t_symbol *a_label; /* symbol to show as label next to box */ + t_symbol *a_symfrom; /* "receive" name -- bind ourselvs to this */ + t_symbol *a_symto; /* "send" name -- send to this on output */ + char a_wherelabel; /* 0-3 for left, right, above, below */ + t_symbol *a_expanded_to; /* a_symto after $0, $1, ... expansion */ +} t_dropdown; + +static void dropdown_redraw(t_gobj *client, t_glist *glist) +{ + t_dropdown *x = (t_dropdown *)client; + glist_retext(x->a_glist, &x->a_text); +} + + /* recolor option offers 0 ignore recolor + 1 recolor */ +static void dropdown_retext(t_dropdown *x, int senditup, int recolor) +{ + t_canvas *canvas = glist_getcanvas(x->a_glist); + t_rtext *y = glist_findrtext(x->a_glist, &x->a_text); + if (recolor) + { + /* not sure if we need to activate dropdown */ + //gui_vmess("gui_gatom_activate", "xsi", + // canvas, rtext_gettag(y), 0); + post("note: dropdown is being activated!"); + } + binbuf_clear(x->a_text.te_binbuf); + binbuf_add(x->a_text.te_binbuf, 1, binbuf_getvec(x->a_names) + x->a_index); + + if (senditup && glist_isvisible(x->a_glist)) + sys_queuegui(x, x->a_glist, dropdown_redraw); +} + +/* use this to keep the index within the correct range */ +static int dropdown_clipindex(t_dropdown *x, int i) +{ + int ret = i, len = binbuf_getnatom(x->a_names); + if (ret < 0) ret = 0; + if (ret > len - 1) ret = len - 1; + return ret; +} + +static void dropdown_set(t_dropdown *x, t_symbol *s, int argc, t_atom *argv) +{ + int oldindex = x->a_index; + if (!argc) return; + x->a_index = dropdown_clipindex(x, (int)atom_getfloat(argv)); + if (oldindex != x->a_index) + dropdown_retext(x, 1, 0); +} + +static int dropdown_names_getmaxwidth(t_dropdown *x) { + char buf[MAXPDSTRING]; + t_binbuf *names = x->a_names, *b = binbuf_new(); + int len = binbuf_getnatom(x->a_names), maxwidth = 0; + while (len--) + { + int width; + binbuf_clear(b); + binbuf_add(b, 1, binbuf_getvec(names) + len); + binbuf_gettext(b, &buf, &width); + if (width > maxwidth) maxwidth = width; + } + return maxwidth; +} + +static void dropdown_names(t_dropdown *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_clear(x->a_names); + if (argc) + binbuf_add(x->a_names, argc, argv); + else + binbuf_addv(x->a_names, "s", &s_); + /* nudge a_index back into range */ + x->a_index = dropdown_clipindex(x, x->a_index); + post("max width is %d", dropdown_names_getmaxwidth(x)); + x->a_maxnamewidth = dropdown_names_getmaxwidth(x); + //dropdown_max_namelength(x); + dropdown_retext(x, 1, 0); +} + +static void dropdown_bang(t_dropdown *x) +{ + t_atom at; + if (x->a_outtype == 0) + SETFLOAT(&at, (t_float)x->a_index); + else + { + t_atom *atfrom = binbuf_getvec(x->a_names) + x->a_index; + if (atfrom->a_type == A_FLOAT) + SETFLOAT(&at, atom_getfloat(atfrom)); + else if (atfrom->a_type == A_SYMBOL) + SETSYMBOL(&at, atom_getsymbol(atfrom)); + else pd_error(x, "only float and symbol output supported"); + } + if (x->a_text.te_outlet) + outlet_list(x->a_text.te_outlet, &s_list, 1, &at); + if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing) + { + if (x->a_symto == x->a_symfrom) + pd_error(x, + "%s: atom with same send/receive name (infinite loop)", + x->a_symto->s_name); + else pd_list(x->a_expanded_to->s_thing, &s_list, 1, &at); + } +} + +static void dropdown_float(t_dropdown *x, t_float f) +{ + /* this should output the atom at the relevant index + Let's do it js-style, negative indices for wrapping + back around and bring numbers greater than last index + down to last index */ + t_atom at; + SETFLOAT(&at, f); + dropdown_set(x, 0, 1, &at); + dropdown_bang(x); +} + +static void dropdown_symbol(t_dropdown *x, t_symbol *s) +{ + t_atom at; + SETSYMBOL(&at, s); + dropdown_set(x, 0, 1, &at); + dropdown_bang(x); +} + + /* We need a list method because, since there's both an "inlet" and a + "nofirstin" flag, the standard list behavior gets confused. */ +static void dropdown_list(t_dropdown *x, t_symbol *s, int argc, t_atom *argv) +{ + if (!argc) + dropdown_bang(x); + else if (argv->a_type == A_FLOAT) + dropdown_float(x, argv->a_w.w_float); + else if (argv->a_type == A_SYMBOL) + dropdown_symbol(x, argv->a_w.w_symbol); + else pd_error(x, "dropdown_list: need float or symbol"); +} + +/* this should send a message to the GUI triggering the dropdown + <div> to be displayed and its event listeners activated */ +static int dropdown_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_dropdown *x = (t_dropdown *)z; + t_canvas *canvas = glist_getcanvas(glist); + t_rtext *y = glist_findrtext(glist, (t_text *)x); + if (doit) + { + int i, len = binbuf_getnatom(x->a_names); + t_atom *at = binbuf_getvec(x->a_names); + /* for gatom we turn the text red to indicate it as editable. + For dropdown we instead have the GUI create a menu with which + the user can choose an option */ + gui_start_vmess("gui_dropdown_activate", "xxsiii", + canvas, + x, + rtext_gettag(y), + x->a_index, + sys_hostfontsize(glist_getfont(glist)), + 1); + gui_start_array(); + for (i = 0; i < len; i++) + { + if (at[i].a_type == A_FLOAT) + gui_f(at[i].a_w.w_float); + else if (at[i].a_type == A_SYMBOL) + gui_s(at[i].a_w.w_symbol->s_name); + else + gui_s("(pointer)"); + } + gui_end_array(); + gui_end_vmess(); + } + return (1); +} + + /* message back from dialog window */ +static void dropdown_param(t_dropdown *x, t_symbol *sel, int argc, t_atom *argv) +{ + /* Check if we need to set an undo point. This happens if the user + clicks the "Ok" button, but not when clicking "Apply" or "Cancel" */ + if (atom_getintarg(7, argc, argv)) + canvas_apply_setundo(x->a_glist, (t_gobj *)x); + + t_float width = atom_getfloatarg(0, argc, argv); + + int output = (int)atom_getfloatarg(1, argc, argv); + t_float dummy = atom_getfloatarg(2, argc, argv); + t_symbol *label = gatom_unescapit(atom_getsymbolarg(3, argc, argv)); + t_float wherelabel = atom_getfloatarg(4, argc, argv); + t_symbol *symfrom = gatom_unescapit(atom_getsymbolarg(5, argc, argv)); + t_symbol *symto = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); + + gobj_vis(&x->a_text.te_g, x->a_glist, 0); + if (!*symfrom->s_name && *x->a_symfrom->s_name) + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + else if (*symfrom->s_name && !*x->a_symfrom->s_name && x->a_text.te_inlet) + { + canvas_deletelinesforio(x->a_glist, &x->a_text, + x->a_text.te_inlet, 0); + inlet_free(x->a_text.te_inlet); + } + if (!*symto->s_name && *x->a_symto->s_name) + outlet_new(&x->a_text, 0); + else if (*symto->s_name && !*x->a_symto->s_name && x->a_text.te_outlet) + { + canvas_deletelinesforio(x->a_glist, &x->a_text, + 0, x->a_text.te_outlet); + outlet_free(x->a_text.te_outlet); + } + x->a_outtype = output; + x->a_dummy = dummy; + if (width < 0) + width = 4; + else if (width > 80) + width = 80; + x->a_text.te_width = width; + x->a_wherelabel = ((int)wherelabel & 3); + x->a_label = label; + if (*x->a_symfrom->s_name) + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + x->a_symfrom = symfrom; + if (*x->a_symfrom->s_name) + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + x->a_symto = symto; + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); + gobj_vis(&x->a_text.te_g, x->a_glist, 1); + gobj_select(&x->a_text.te_g, x->a_glist, 1); + canvas_dirty(x->a_glist, 1); + canvas_getscroll(x->a_glist); + /* glist_retext(x->a_glist, &x->a_text); */ +} + + /* ---------------- dropdown-specific widget functions --------------- */ + +/* this can be combined with gatom_getwherelabel */ +static void dropdown_getwherelabel(t_dropdown *x, t_glist *glist, int *xp, int *yp) +{ + int x1, y1, x2, y2; + text_getrect(&x->a_text.te_g, glist, &x1, &y1, &x2, &y2); + if (x->a_wherelabel == ATOM_LABELLEFT) + { + *xp = -3 - + strlen(canvas_realizedollar(x->a_glist, x->a_label)->s_name) * + sys_fontwidth(glist_getfont(glist)); + *yp = y2 - y1 - 4; + } + else if (x->a_wherelabel == ATOM_LABELRIGHT) + { + *xp = x2 - x1 + 3; + *yp = y2 - y1 - 4; + } + else if (x->a_wherelabel == ATOM_LABELUP) + { + *xp = -1; + *yp = -3; + } + else + { + *xp = -1; + *yp = y2 - y1 + sys_fontheight(glist_getfont(glist)); + } +} + +/* for dropdown's label */ +static void dropdown_vis(t_gobj *z, t_glist *glist, int vis) +{ + //fprintf(stderr,"dropdown_vis\n"); + t_dropdown *x = (t_dropdown *)z; + text_vis(z, glist, vis); + if (*x->a_label->s_name) + { + if (vis) + { + int x1, y1; + t_rtext *y = glist_findrtext(x->a_glist, &x->a_text); + dropdown_getwherelabel(x, glist, &x1, &y1); + gui_vmess("gui_text_new", "xssiiisi", + glist_getcanvas(glist), + rtext_gettag(y), + "dropdown", + 0, + x1, // left margin + y1, // top margin + canvas_realizedollar(x->a_glist, x->a_label)->s_name, + sys_hostfontsize(glist_getfont(glist)) + ); + } + else + { + /* We're just deleting the parent gobj in the GUI, which takes + care of removing all the children. So we don't need to send + a message here */ + //sys_vgui(".x%lx.c delete %lx.l\n", glist_getcanvas(glist), x); + } + } + if (!vis) + sys_unqueuegui(x); +} + +/* a lot of this is duplicated from canvas_atom-- we should factor out the + common stuff from the copy/pasta here */ +void canvas_dropdown(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + char tagbuf[MAXPDSTRING]; + if (canvas_hasarray(gl)) return; + //fprintf(stderr,"canvas_atom\n"); + t_dropdown *x = (t_dropdown *)pd_new(dropdown_class); + x->a_text.te_width = 0; /* don't know it yet. */ + x->a_text.te_type = T_ATOM; + x->a_text.te_iemgui = 0; + x->a_text.te_binbuf = binbuf_new(); + x->a_glist = gl; + x->a_dummy = 0; + x->a_outtype = 1; /* output value by default */ + x->a_wherelabel = 0; + x->a_label = &s_; + x->a_symfrom = &s_; + x->a_symto = x->a_expanded_to = &s_; + x->a_index = 0; + x->a_names = binbuf_new(); + binbuf_addv(x->a_names, "ssss", &s_symbol, &s_float, &s_bang, &s_list); + x->a_maxnamewidth = dropdown_names_getmaxwidth(x); + x->a_text.te_width = 6; + + /* bind symbol for sending index updates from the GUI */ + sprintf(tagbuf, "x%lx", (long unsigned int)x); + pd_bind(&x->a_text.te_pd, gensym(tagbuf)); + + binbuf_add(x->a_text.te_binbuf, 1, binbuf_getvec(x->a_names)); + if (argc > 1) + /* create from file. x, y, width, low-range, high-range, flags, + label, receive-name, send-name */ + { + x->a_text.te_xpix = atom_getfloatarg(0, argc, argv); + x->a_text.te_ypix = atom_getfloatarg(1, argc, argv); + x->a_text.te_width = atom_getintarg(2, argc, argv); + /* sanity check because some very old patches have trash in this + field... remove this in 2003 or so: */ + if (x->a_text.te_width < 0 || x->a_text.te_width > 500) + x->a_text.te_width = 4; + x->a_outtype = (int)atom_getfloatarg(3, argc, argv); + x->a_dummy = atom_getfloatarg(4, argc, argv); + x->a_wherelabel = (((int)atom_getfloatarg(5, argc, argv)) & 3); + x->a_label = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); + x->a_symfrom = gatom_unescapit(atom_getsymbolarg(7, argc, argv)); + if (*x->a_symfrom->s_name) + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + + x->a_symto = gatom_unescapit(atom_getsymbolarg(8, argc, argv)); + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); + if (x->a_symto == &s_) + outlet_new(&x->a_text, &s_float); + if (x->a_symfrom == &s_) + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + glist_add(gl, &x->a_text.te_g); + } + else + { + int connectme, xpix, ypix, indx, nobj; + canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj); + outlet_new(&x->a_text, &s_float); + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + x->a_text.te_xpix = xpix; + x->a_text.te_ypix = ypix; + glist_add(gl, &x->a_text.te_g); + glist_noselect(gl); + glist_select(gl, &x->a_text.te_g); + if (connectme == 1) + canvas_connect(gl, indx, 0, nobj, 0); + else if (connectme == 0) + { + canvas_displaceselection(glist_getcanvas(gl), -8, -8); + canvas_startmotion(glist_getcanvas(gl)); + } + //canvas_setundo(glist_getcanvas(gl), + // canvas_undo_create, canvas_undo_set_create(gl), "create"); + canvas_undo_add(glist_getcanvas(gl), 9, "create", + (void *)canvas_undo_set_create(glist_getcanvas(gl))); + } + glob_preset_node_list_seek_hub(); + glob_preset_node_list_check_loc_and_update(); +} + +static void dropdown_free(t_dropdown *x) +{ + char tagbuf[MAXPDSTRING]; + sprintf(tagbuf, "x%lx", (long unsigned int)x); + pd_unbind(&x->a_text.te_pd, gensym(tagbuf)); + + if (*x->a_symfrom->s_name) + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + gfxstub_deleteforkey(x); +} + +static void dropdown_properties(t_gobj *z, t_glist *owner) +{ + t_dropdown *x = (t_dropdown *)z; + //char buf[200]; + //sprintf(buf, "pdtk_dropdown_dialog %%s %d %g %g %d {%s} {%s} {%s}\n", + // x->a_text.te_width, x->a_draglo, x->a_draghi, + // x->a_wherelabel, gatom_escapit(x->a_label)->s_name, + // gatom_escapit(x->a_symfrom)->s_name, + // gatom_escapit(x->a_symto)->s_name); + //gfxstub_new(&x->a_text.te_pd, x, buf); + gui_start_vmess("gui_dropdown_dialog", "s", + gfxstub_new2(&x->a_text.te_pd, x)); + gui_start_array(); + gui_s("name"); gui_s("dropdown"); + gui_s("width"); gui_i(x->a_text.te_width); + gui_s("outtype"); gui_f(x->a_outtype); + gui_s("labelpos"); gui_i(x->a_wherelabel); + gui_s("label"); gui_s(gatom_escapit(x->a_label)->s_name); + gui_s("receive_symbol"); gui_s(gatom_escapit(x->a_symfrom)->s_name); + gui_s("send_symbol"); gui_s(gatom_escapit(x->a_symto)->s_name); + gui_end_array(); + gui_end_vmess(); +} + /* -------------------- widget behavior for text objects ------------ */ /* variant of the glist_findrtext found in g_rtext.c @@ -1419,6 +1856,9 @@ static void text_getrect(t_gobj *z, t_glist *glist, int font = glist_getfont(glist); int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); width = (x->te_width > 0 ? x->te_width : 6) * fontwidth + 2; + /* add an extra two characters for the dropdown box's arrow */ + if (pd_class(&x->te_pd) == dropdown_class) + width += fontwidth * 2; height = fontheight + 3; /* borrowed from TMARGIN, etc, in g_rtext.c */ } // jsarlo @@ -1456,6 +1896,13 @@ static void text_getrect(t_gobj *z, t_glist *glist, if (y) { width = rtext_width(y); + if (pd_class(&x->te_pd) == dropdown_class) + { + int font = glist_getfont(glist); + int fontwidth = sys_fontwidth(font); +// width += fontwidth * 2; + width = fontwidth * (((t_dropdown *)x)->a_maxnamewidth + 2); + } height = rtext_height(y) - (iscomment << 1); } @@ -1591,15 +2038,6 @@ static void text_displace_withtag(t_gobj *z, t_glist *glist, } } -static void gatom_displace_withtag(t_gobj *z, t_glist *glist, - int dx, int dy) -{ - //t_gatom *x = (t_gatom*)z; - text_displace_withtag(z, glist, dx, dy); - //sys_vgui(".x%lx.c move %lx.l %d %d\n", glist_getcanvas(glist), - // x, dx, dy); -} - static void text_select(t_gobj *z, t_glist *glist, int state) { t_text *x = (t_text *)z; @@ -1723,7 +2161,8 @@ static void text_activate(t_gobj *z, t_glist *glist, int state) { t_text *x = (t_text *)z; t_rtext *y = glist_findrtext(glist, x); - if (z->g_pd != gatom_class) rtext_activate(y, state); + if (z->g_pd != gatom_class && z->g_pd != dropdown_class) + rtext_activate(y, state); } static void text_delete(t_gobj *z, t_glist *glist) @@ -1825,6 +2264,7 @@ static int text_click(t_gobj *z, struct _glist *glist, } else if (x->te_type == T_ATOM) { + /* Note: dropdown has its own click handler */ t_canvas *canvas = glist_getcanvas(glist); t_rtext *y = glist_findrtext(glist, x); if (doit) @@ -1886,19 +2326,35 @@ void text_save(t_gobj *z, t_binbuf *b) else if (x->te_type == T_ATOM) { //fprintf(stderr, "atom\n"); - t_atomtype t = ((t_gatom *)x)->a_atom.a_type; - t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") : - (t == A_FLOAT ? gensym("floatatom") : gensym("intatom"))); - t_symbol *label = gatom_escapit(((t_gatom *)x)->a_label); - t_symbol *symfrom = gatom_escapit(((t_gatom *)x)->a_symfrom); - t_symbol *symto = gatom_escapit(((t_gatom *)x)->a_symto); - binbuf_addv(b, "ssiiifffsss", gensym("#X"), sel, - (int)x->te_xpix, (int)x->te_ypix, (int)x->te_width, - (double)((t_gatom *)x)->a_draglo, - (double)((t_gatom *)x)->a_draghi, - (double)((t_gatom *)x)->a_wherelabel, - label, symfrom, symto); - } + if (pd_class(&x->te_pd) == gatom_class) + { + t_atomtype t = ((t_gatom *)x)->a_atom.a_type; + t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") : + (t == A_FLOAT ? gensym("floatatom") : gensym("intatom"))); + t_symbol *label = gatom_escapit(((t_gatom *)x)->a_label); + t_symbol *symfrom = gatom_escapit(((t_gatom *)x)->a_symfrom); + t_symbol *symto = gatom_escapit(((t_gatom *)x)->a_symto); + binbuf_addv(b, "ssiiifffsss", gensym("#X"), sel, + (int)x->te_xpix, (int)x->te_ypix, (int)x->te_width, + (double)((t_gatom *)x)->a_draglo, + (double)((t_gatom *)x)->a_draghi, + (double)((t_gatom *)x)->a_wherelabel, + label, symfrom, symto); + } + else + { + t_symbol *sel = gensym("dropdown"); + t_symbol *label = gatom_escapit(((t_dropdown *)x)->a_label); + t_symbol *symfrom = gatom_escapit(((t_dropdown *)x)->a_symfrom); + t_symbol *symto = gatom_escapit(((t_dropdown *)x)->a_symto); + binbuf_addv(b, "ssiiiiffsss", gensym("#X"), sel, + (int)x->te_xpix, (int)x->te_ypix, (int)x->te_width, + (int)((t_dropdown *)x)->a_outtype, + (double)((t_dropdown *)x)->a_dummy, + (double)((t_dropdown *)x)->a_wherelabel, + label, symfrom, symto); + } + } else { //fprintf(stderr,"comment\n"); @@ -1959,7 +2415,19 @@ static t_widgetbehavior gatom_widgetbehavior = text_delete, gatom_vis, text_click, - gatom_displace_withtag, + text_displace_withtag, +}; + +static t_widgetbehavior dropdown_widgetbehavior = +{ + text_getrect, + text_displace, + text_select, + text_activate, + text_delete, + dropdown_vis, + dropdown_click, + text_displace_withtag, }; /* -------------------- the "text" class ------------ */ @@ -2138,7 +2606,6 @@ void glist_drawiofor_withtag(t_glist *glist, t_object *ob, int firsttime, } } - void text_drawborder(t_text *x, t_glist *glist, char *tag, int width2, int height2, int firsttime) { @@ -2259,9 +2726,11 @@ void text_drawborder(t_text *x, t_glist *glist, // (selected ? "$pd_colors(selection)" : "$pd_colors(atom_box_border)"), // tag, tag, (selected ? "selected" : "")); /* These coords can be greatly simplified... */ - gui_vmess("gui_atom_draw_border", "xsii", + gui_vmess("gui_atom_draw_border", "xsiii", glist_getcanvas(glist), tag, + pd_class(&x->te_pd) == dropdown_class ? + ((t_dropdown *)x)->a_outtype + 1 : 0, x2 - x1, y2 - y1); } @@ -2272,9 +2741,10 @@ void text_drawborder(t_text *x, t_glist *glist, // "%d %d %d %d %d %d %d %d %d %d %d %d\n", // glist_getcanvas(glist), tag, // x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1); - gui_vmess("gui_atom_redraw_border", "xsii", + gui_vmess("gui_atom_redraw_border", "xsiii", glist_getcanvas(glist), tag, + pd_class(&x->te_pd) == dropdown_class ? 1 : 0, x2 - x1, y2 - y1); } @@ -2432,7 +2902,6 @@ void text_drawborder_withtag(t_text *x, t_glist *glist, // (long unsigned int)glist_getcanvas(glist)); } - void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag) { //fprintf(stderr,"glist_eraseiofor\n"); @@ -2746,6 +3215,21 @@ void g_text_setup(void) A_GIMME, 0); class_setwidget(gatom_class, &gatom_widgetbehavior); class_setpropertiesfn(gatom_class, gatom_properties); -} - + dropdown_class = class_new(gensym("dropdown"), 0, (t_method)dropdown_free, + sizeof(t_dropdown), CLASS_NOINLET | CLASS_PATCHABLE, 0); + class_addbang(dropdown_class, dropdown_bang); + class_addfloat(dropdown_class, dropdown_float); + class_addsymbol(dropdown_class, dropdown_symbol); + class_addlist(dropdown_class, dropdown_list); + class_addmethod(dropdown_class, (t_method)dropdown_set, gensym("set"), + A_GIMME, 0); + class_addmethod(dropdown_class, (t_method)dropdown_names, gensym("names"), + A_GIMME, 0); + //class_addmethod(dropdown_class, (t_method)dropdown_click, gensym("click"), + // A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(dropdown_class, (t_method)dropdown_param, gensym("param"), + A_GIMME, 0); + class_setwidget(dropdown_class, &dropdown_widgetbehavior); + class_setpropertiesfn(dropdown_class, dropdown_properties); +}