PopupMenu constructor
Shows a popup menu above the parent
element (e.g. a div) with:
id
The popup may optionally get an id (should be unique if the popup
is to be identified with it).
htmlItemTexts
The menu items texts. The format is:
"textDiaUtils.SEP_STANaction code". If action code = "TITLE", then
this is not an action, but is displayed as a popup title.
If text starts with POPUP_SPAN_GREYOUT, the menu item is displayed,
but greyed out. If the user clicks on it, no action is performed.
buttontypes
If null, no special item style is applied to any menu item.
If not null, the list must have the same length as htmlItemTexts
.
If a list entry is null, no special style is applied to the respective
menu item. If a list entry is DiaAttr.CHECKBOX, a checkbox element is
displayed in front of the item text.
NOTE: Currently the checked box is provided only to display a status,
not react on a checkbox state change. See below.
isChecked
Must be null, or have the same length as buttontypes
. The
list members may take on the value null or DiaUtils.FALSE or
DiaUtils.TRUE. This defines whether the respective check button
of buttontypes
is show as "unchecked" or "checked".
popupCallback
will be called when an popup item is clicked,
x
, y
are the top left corner coordinates of the popup.
preventDefault
if false: finger-scrolling the items is enabled for
touch screens.
Implementation
PopupMenu(
Element parent,
String id,
List<String> htmlItemTexts,
List<String> buttontypes,
List<String> isChecked,
BaseDialogCloseCallback popupCallback,
int x,
int y,
bool preventDefault):super.noModal(popupCallback){
final int NCOLS = 2; // adjust if necessary!
int nitems = htmlItemTexts.length;
dia.id = id;
// Make sure the popup is always displayed above everything else by giving it a big z-index.
// Especially over the jsme structure display div.
setPopupStyle(dia, null);
Map<String, String> attr = DiaAttr.attr; // use shortcut
// nodify standad style:
parent.append(dia);
diaTable.style
..borderCollapse = "collapse"
..marginTop = "6px"
..marginBottom = "0px";
dia
..append(diaTable)
..onTouchMove.listen((e) {
// Needed on iPad: Otherwise moving the finger over the dialog would move
// the whole browser content up.
// This feature is very good if a text entry field are present,
// to be able to enter text if covered by the keyboard. In our popup
// case that's not necessary, but rather
// disturbing.
if (preventDefault) e.preventDefault();
// lastTouchedActionCode = null;
// lastTouchPoints = getTouchPoints(e);
});
// TableRowElement row;
TableCellElement cell;
List<TableRowElement> rows = List<TableRowElement>();
void addEmptyTableRow() {
rows.add(TableRowElement());
cell = TableCellElement();
DiaUtils.appendHtml2(cell, ' ');
cell.colSpan = NCOLS; // 1 empty row
rows.last.append(cell);
diaTable.append(rows.last);
}
// add table header
// decode: "displayed text||action code"
String displayedText = htmlItemTexts[0].split(DiaUtils.SEP_STAN)[0];
String actionCode = htmlItemTexts[0].split(DiaUtils.SEP_STAN)[1];
int itemstart = 0;
if (actionCode == "TITLE") {
rows.add(TableRowElement());
cell = TableCellElement();
DiaUtils.appendHtml2(cell, displayedText);
cell.colSpan = NCOLS; // the htmlInfoText spans NCOLS columns
cell.style
..textAlign = "center"
..color = attr[DiaAttr.DIALOG_HEADER_COLOR]
..fontSize = "${attr[DiaAttr.DIALOG_HEADER_FONTSIZE]}px";
rows.last.append(cell);
diaTable.append(rows.last);
addEmptyTableRow();
itemstart = 1;
}
for (int i = itemstart; i < nitems; i++) {
// NOTE: for any one reason, the type TableRowElement must be specified inside the loop,
// otherwsie
rows.add(TableRowElement());
TableRowElement row = rows.last;
row.style.color = attr[DiaAttr.DIALOG_TEXT_COLOR]; // initial setting
row.onMouseEnter.listen((MouseEvent event) {
row.style
..backgroundColor = attr[DiaAttr.POPUP_SELECTION_COLOR]
..color = "white";
});
row.onMouseLeave.listen((MouseEvent event) {
row.style
..backgroundColor = attr[DiaAttr.POPUP_BACKGROUND]
..color = attr[DiaAttr.DIALOG_TEXT_COLOR];
});
// format is: "displayed text||action code"
String displayedText = htmlItemTexts[i].split(DiaUtils.SEP_STAN)[0];
String actionCode = htmlItemTexts[i].split(DiaUtils.SEP_STAN)[1];
// the first column may contain checkbox or more
TableCellElement cell = TableCellElement();
cell.id = actionCode;
if (buttontypes != null && isChecked != null) {
if (buttontypes[i] == DiaAttr.CHECKBOX &&
isChecked[i] != null &&
(isChecked[i] == DiaUtils.TRUE || isChecked[i] == DiaUtils.FALSE)) {
InputElement cbox = InputElement(type: DiaAttr.CHECKBOX);
cbox.checked = DiaUtils.getBoolFromString(isChecked[i]);
// cbox.value = "checked"; // TODO ???
cbox.style.fontSize = "${attr[DiaAttr.DIALOG_POPUP_FONTSIZE2]}px";
cell.append(cbox);
}
// else should not occur
} else {
// first column remains empty for now
cell.appendText("");
cell.style
..textAlign = "left"
..cursor = "pointer"
..padding = "7px"
..margin = ".5em 1em";
}
void execute(String curaction) {
// only a non-greyed item will be executed!
if (dia != null && !displayedText.contains(POPUP_SPAN_GREYOUT)) {
close(UserInput(curaction, null, null));
dia = null;
}
}
void handleTouchStart(TouchEvent e) {
lastTouchPoints = DiaUtils.getTouchPoints(e);
}
void handleTouchEnd(TouchEvent e) {
Element eventCell = e.target;
String curaction = eventCell.id;
TouchList tl = e.changedTouches;
if (tl == null || tl.isEmpty) return;
int lastx = lastTouchPoints[0].x;
int lasty = lastTouchPoints[0].y;
// InfoDialog("touchend=${eventCell.style.width} ${eventCell.style.height} $lastx $lasty ${tl.first.page.x} ${tl.first.page.y}", null);
if ((lastx - tl.first.page.x).abs() > 30 ||
(lasty - tl.first.page.y).abs() > 20) {
return;
}
execute(curaction);
}
if (!DiaUtils.hasMouse()) {
cell.onTouchStart.listen((TouchEvent e) {
handleTouchStart(e);
});
cell.onTouchEnd.listen((TouchEvent e) {
// Note: e.preventDefault() for sidebar div handled in SpecPad.
handleTouchEnd(e);
});
} else {
// this would execute when clicking the first column
cell.onClick.listen((MouseEvent e) {
// print("popup clicked row $i");
Element eventCell = e.target;
String curaction = eventCell.id;
execute(curaction);
});
}
row.append(cell);
// these are the action descriptions
cell = TableCellElement();
cell.id = actionCode;
DiaUtils.appendHtml2(cell, displayedText);
cell.style
..textAlign = "left"
..cursor = "pointer"
..paddingBottom = attr[DiaAttr
.DIALOG_POPUP_TEXT_CELL_PADDING] // this and next define the final cell height
..paddingTop = attr[DiaAttr.DIALOG_POPUP_TEXT_CELL_PADDING]
// ..margin = ".5em 1em"
..fontSize = "${attr[DiaAttr.DIALOG_POPUP_FONTSIZE2]}px";
DiaUtils.appendHtml2(cell, "<br>");
if (!DiaUtils.hasMouse()) {
cell.onTouchStart.listen((TouchEvent e) {
handleTouchStart(e);
});
cell.onTouchEnd.listen((TouchEvent e) {
// Note: e.preventDefault() for sidebar div handled in SpecPad.
handleTouchEnd(e);
});
} else {
cell.onClick.listen((MouseEvent e) {
Element eventCell = e.target;
String curaction = eventCell.id;
execute(curaction);
});
}
row.append(cell);
diaTable.append(row);
// diaTable.append(createSep(row.style.width));
} // for
dia.style
..backgroundColor = attr[DiaAttr.POPUP_BACKGROUND]
..background = attr[DiaAttr.POPUP_BACKGROUND]
..position = "fixed"
..left = "${x}px"
..top = "${y}px"
..marginLeft = "0px"
..marginTop = "0px"
..paddingTop = "0px"
..paddingLeft = "0px"
..width = "${diaTable.clientWidth + 6}px" // like table, +6
..opacity = "1"
..zIndex = "10000000" // must be above everything
;
}