Commit d82ccc36 authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

Merge branch 'fix-dropdown'

parents 5bfda305 774db790
......@@ -160,6 +160,8 @@ var canvas_events = (function() {
last_draggable_y, // last y
previous_state = "none", /* last state, excluding explicit 'none' */
match_words_state = false,
last_dropdown_menu_x,
last_dropdown_menu_y,
last_search_term = "",
svg_view = document.getElementById("patchsvg").viewBox.baseVal,
textbox = function () {
......@@ -208,21 +210,44 @@ var canvas_events = (function() {
svg.setAttribute("height", h);
},
dropdown_index_to_pd = function(elem) {
pdgui.pdsend(elem.getAttribute("data-callback"),
elem.querySelector(".highlighted").getAttribute("data-index"));
var highlighted = elem.querySelector(".highlighted");
if (highlighted) {
pdgui.pdsend(elem.getAttribute("data-callback"),
highlighted.getAttribute("data-index"));
}
},
dropdown_highlight_elem = function(elem) {
dropdown_clear_highlight = function() {
var container = document.querySelector("#dropdown_list"),
li_array;
if (!elem.classList.contains("highlighted")) {
li_array = container.querySelectorAll("li");
Array.prototype.forEach.call(li_array, function(e) {
e.classList.remove("highlighted");
});
Array.prototype.forEach.call(li_array, function(e) {
e.classList.remove("highlighted");
});
},
dropdown_highlight_elem = function(elem, scroll) {
var container = document.querySelector("#dropdown_list");
if (!elem.classList.contains("highlighted")) {
dropdown_clear_highlight();
elem.classList.add("highlighted");
// Make sure the highlighted element is in view
container.scrollTop = elem.offsetTop + elem.offsetHeight
- container.clientHeight;
if (scroll) {
if (elem.offsetTop < container.scrollTop
|| elem.offsetTop + elem.offsetHeight >
container.scrollTop + container.offsetHeight) {
if (scroll === "up") {
// we can't usse elem.scrollIntoView() here
// because it may also change the scrollbar on
// the document, which in turn could change the
// pageX/pageY, for the mousemove event, which
// in turn would make it impossible for us to
// filter out unnecessary mousemove calls
// elem.scrollIntoView();
container.scrollTop = elem.offsetTop;
} else if (scroll === "down") {
container.scrollTop = elem.offsetTop + elem.offsetHeight
- container.clientHeight;
}
}
}
}
},
events = {
......@@ -494,13 +519,13 @@ var canvas_events = (function() {
iemgui_label_mouseup: function(evt) {
//pdgui.post("lifting the mousebutton on an iemgui label");
// Set last state (none doesn't count as a state)
//pdgui.post("previous state is " + canvas_events.get_previous_state());
//pdgui.post("previous state is "
// + canvas_events.get_previous_state());
canvas_events[canvas_events.get_previous_state()]();
},
dropdown_menu_keydown: function(evt) {
var select_elem = document.querySelector("#dropdown_list"),
li;
pdgui.post("keycode is " + evt.keyCode);
switch(evt.keyCode) {
case 13:
case 32:
......@@ -510,34 +535,43 @@ var canvas_events = (function() {
break;
case 27: // escape
select_elem.style.setProperty("display", "none");
pdgui.post("canceled thing");
canvas_events.normal();
break;
case 38: // up
li = select_elem.querySelector(".highlighted");
li = li.previousElementSibling ||
li.parentElement.lastElementChild;
dropdown_highlight_elem(li);
dropdown_highlight_elem(li, "up");
evt.preventDefault();
break;
case 40: // down
li = select_elem.querySelector(".highlighted");
li = li.nextElementSibling ||
li.parentElement.firstElementChild;
dropdown_highlight_elem(li);
dropdown_highlight_elem(li, "down");
evt.preventDefault();
break;
default:
}
},
dropdown_menu_keypress: function(evt) {
var li_nodes = document.querySelectorAll("#dropdown_list li"),
string_array = [],
highlighted,
highlighted_index,
match,
offset;
highlighted_index =
+document.querySelector("#dropdown_list .highlighted")
.getAttribute("data-index");
offset = highlighted_index + 1;
highlighted = document
.querySelector("#dropdown_list .highlighted");
if (highlighted) {
highlighted_index =
+document.querySelector("#dropdown_list .highlighted")
.getAttribute("data-index");
offset = highlighted_index + 1;
} else {
highlighted_index = 1;
offset = 2;
}
Array.prototype.forEach.call(li_nodes, function(e, i, a) {
var s = a[(i + offset) % a.length];
string_array.push(s.textContent.trim());
......@@ -550,23 +584,49 @@ var canvas_events = (function() {
if (match !== undefined) {
match = (match + offset) % li_nodes.length;
if (match !== highlighted_index) {
dropdown_highlight_elem(li_nodes[match]);
dropdown_highlight_elem(li_nodes[match],
match < highlighted_index ? "up" : "down");
}
}
},
dropdown_menu_mousedown: function(evt) {
var select_elem = document.querySelector("#dropdown_list");
var select_elem = document.querySelector("#dropdown_list"),
in_dropdown = evt.target;
while (in_dropdown) {
if (in_dropdown.id === "dropdown_list") {
break;
}
in_dropdown = in_dropdown.parentNode;
}
// Allow scrollbar click and drag without closing the menu
if (in_dropdown &&
evt.pageX - select_elem.offsetLeft >
select_elem.clientWidth) {
return;
}
// Special case for OSX, where the scrollbar doesn't take
// up any extra space
if (nw.process.platform === "darwin"
&& (evt.target.id === "dropdown_list")) {
return;
}
if (evt.target.parentNode
&& evt.target.parentNode.parentNode
&& evt.target.parentNode.parentNode.id === "dropdown_list") {
dropdown_highlight_elem(evt.target);
}
// This selects whatever item is highlighted even
// if we click outside the menu. Might be better to
// cancel in that case.
dropdown_index_to_pd(select_elem);
select_elem.style.setProperty("display", "none");
canvas_events.normal();
},
dropdown_menu_mouseup: function(evt) {
var i, select_elem;
// This can be triggered if the user keeps the mouse down
// to highlight an element and releases the mouse button to
// choose that element
if (evt.target.parentNode
&& evt.target.parentNode.parentNode
&& evt.target.parentNode.parentNode.id === "dropdown_list") {
......@@ -576,14 +636,33 @@ var canvas_events = (function() {
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") {
dropdown_highlight_elem(evt.target);
dropdown_menu_wheel: function(evt) {
// Here we generate bogus mouse coords so that
// we can break through the filter below if we're
// using the mouse wheel to scroll in the list.
last_dropdown_menu_x = Number.MIN_VALUE;
last_dropdown_menu_y = Number.MIN_VALUE;
},
dropdown_menu_mousemove: function(evt) {
// For whatever reason, Chromium decides to trigger the
// mousemove/mouseenter/mouseover events if the element
// underneath it changes (or for mousemove, if the element
// moves at all). Unfortunately that means we have to track
// the mouse position the entire time to filter out the
// true mousemove events from the ones Chromium generates
// when a scroll event changes the element under the mouse.
if (evt.pageX !== last_dropdown_menu_x
|| evt.pageY !== last_dropdown_menu_y) {
if (evt.target.parentNode
&& evt.target.parentNode.parentNode
&& evt.target.parentNode.parentNode.id === "dropdown_list") {
dropdown_highlight_elem(evt.target);
} else {
dropdown_clear_highlight();
}
}
// hide the dropdown menu <div> thingy
last_dropdown_menu_x = evt.pageX;
last_dropdown_menu_y = evt.pageY;
}
},
utils = {
......@@ -840,9 +919,11 @@ var canvas_events = (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);
document.addEventListener("mousemove", events.dropdown_menu_mousemove, false);
document.addEventListener("keydown", events.dropdown_menu_keydown, false);
document.addEventListener("keypress", events.dropdown_menu_keypress, false);
document.querySelector("#dropdown_list")
.addEventListener("wheel", events.dropdown_menu_wheel, false);
},
search: function() {
this.none();
......
......@@ -4596,7 +4596,13 @@ function dropdown_populate(cid, label_array, current_index) {
}
function gui_dropdown_activate(cid, obj_tag, tag, current_index, font_size, state, label_array) {
var g, select_elem, svg_view;
var g, select_elem, svg_view, g_bbox,
doc_height, // document height, excluding the scrollbar
menu_height, // height of the list of elements inside the div
div_y, // position of div containing the dropdown menu
div_max, // max height of the div
scroll_y,
offset_anchor; // top or bottom
// 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]) {
......@@ -4604,21 +4610,54 @@ function gui_dropdown_activate(cid, obj_tag, tag, current_index, font_size, stat
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");
dropdown_populate(cid, label_array, current_index);
// stick the obj_tag in a data field
select_elem.setAttribute("data-callback", obj_tag);
select_elem.style.setProperty("max-height",
(patchwin[cid].window.innerHeight -
g.getBoundingClientRect().bottom - 5) + "px"
);
// display the menu so we can measure it
g_bbox = g.getBoundingClientRect();
// Measuring the document height is tricky-- the following
// method is the only reliable one I've found. And even here,
// if you display the select_elem as inline _before_ measuring
// the doc height, the result ends up being _smaller_. No idea.
doc_height =
patchwin[cid].window.document.documentElement
.clientHeight;
// Now let's display the select_elem div so we can measure it
select_elem.style.setProperty("display", "inline");
menu_height = select_elem.querySelector("ol")
.getBoundingClientRect().height;
scroll_y = patchwin[cid].window.scrollY;
// If the area below the object is smaller than 75px, then
// display the menu above the object.
// If the entire menu won't fit below the object but _will_
// fit above it, display it above the object.
// If the menu needs a scrollbar, display it below the object
if (doc_height - g_bbox.bottom <= 75
|| (menu_height > doc_height - g_bbox.bottom
&& menu_height <= g_bbox.top)) {
// menu on top
offset_anchor = "bottom";
div_max = g_bbox.top - 2;
div_y = doc_height - (g_bbox.top + scroll_y);
}
else {
// menu on bottom (possibly with scrollbar)
offset_anchor = "top";
div_max = doc_height - g_bbox.bottom - 2;
div_y = g_bbox.bottom + scroll_y;
}
// set a max-height to force scrollbar if needed
select_elem.style.setProperty("max-height", div_max + "px");
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");
// Remove "top" and "bottom" props to keep state clean
select_elem.style.removeProperty("top");
select_elem.style.removeProperty("bottom");
// Now position the div relative to either the "top" or "bottom"
select_elem.style.setProperty(offset_anchor, div_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");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment