Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ The release run heads these entries with the version and opens a fresh
four screens wide, which the browser's own floor of 0.25 cannot reach. An A0
pdf page now zooms out to fit; narrower content is unchanged.

- `odr.editing.setSheetOptions({editOnClick})` states whether a click opens a
sheet cell's editor, because the pointer is a guess: an android WebView
reports a fine one on a touch screen. Unstated, the pointer answers.

- **Fix**: selecting text in a pdf view showed the invisible layer that carries
it, as a second set of glyphs over the drawn ones. That layer now stays
transparent under `::selection`, and states the highlight background, which
Expand Down
3 changes: 2 additions & 1 deletion docs/design/spreadsheet-editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ Each step ships on its own. "Both" means `.ods` and `.xlsx`.
with their code table (decision 7). `spreadsheet_js` publishes `odr.sheet` in
the same step (decision 8) — the position map the mode reads a lock through.
2. **Landed**, with item 3: an editor that drops what is typed is not one.
Overlay editor: double-click / Enter / typing opens it over the cell; Enter,
Overlay editor: double-click / Enter / typing opens it over the cell, and a
single click where `editOnClick` or the pointer says so; Enter,
Tab and blur commit; Escape cancels; arrow keys move the pin, through
`odr.sheet.pin` rather than a pin of its own. A locked cell refuses on the
click rather than on the double click that would have opened it.
Expand Down
32 changes: 29 additions & 3 deletions src/odr/internal/html/frontend/sheet-editing.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,16 +405,42 @@
}
});

/// A coarse pointer opens the editor on one tap: a phone gives up the first
/// tap of a double tap as a click of its own anyway. A fine pointer keeps the
/// double click, because there a single click selects a cell without editing.
/// Gesture policy, the viewer's to set. `editOnClick` unstated asks the
/// pointer.
var options = { editOnClick: null };

/// Whether a click opens the editor, where a double click always does. The
/// pointer answers only while `editOnClick` is unstated, because it is a
/// guess: an android WebView reports a fine one on a touch screen.
function tapEdits() {
// `!= null` so an unset key a host passes reads as unstated, not as off
if (options.editOnClick != null) {
return !!options.editOnClick;
}
return (
typeof window.matchMedia === "function" &&
window.matchMedia("(pointer: coarse)").matches
);
}

/// Merged into what is set; an unknown key throws.
odr.editing.setSheetOptions = function (value) {
Object.keys(value || {}).forEach(function (key) {
if (!Object.prototype.hasOwnProperty.call(options, key)) {
throw new Error("odr.editing: unknown sheet option " + key);
}
options[key] = value[key];
});
};

odr.editing.getSheetOptions = function () {
var copy = {};
Object.keys(options).forEach(function (key) {
copy[key] = options[key];
});
return copy;
};

// A locked cell says so on the click, not on the double click.
table.addEventListener("click", function (event) {
var at =
Expand Down
64 changes: 64 additions & 0 deletions test/browser/sheet/editing.html
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,70 @@
odr.editing.committed();
check("and a save clears them with the log", staleNow() === "");

// --------------------------------------- who says a tap opens the cell

// The pointer is a guess: an android WebView on an emulator reports a
// fine one. So the viewer may state it outright.
// what `tapEdits` answers, read through the one gesture that shows it
function tapEditsNow() {
clickCell(0, 0);
var open = editor() !== null;
odr.editing.disable();
odr.editing.enable();
return open;
}
function clickCell(column, row) {
odr.editing.disable();
odr.editing.enable();
cell(column, row).dispatchEvent(
new MouseEvent("click", { bubbles: true, detail: 1 })
);
}

check(
"the pointer answers until the viewer states it",
odr.editing.getSheetOptions().editOnClick === null
);

odr.editing.setSheetOptions({ editOnClick: false });
clickCell(2, 2);
check("stated off, one click opens nothing", editor() === null);
cell(2, 2).dispatchEvent(new MouseEvent("dblclick", { bubbles: true, detail: 2 }));
check("and the double click still opens the cell", editor() !== null);

odr.editing.setSheetOptions({ editOnClick: true });
clickCell(0, 0);
check("stated on, one click opens it", editor() !== null);
check("holding what the cell shows", editor().value === "a string that runs on");
check(
"and the cell is pinned under it",
cell(0, 0).classList.contains("odr-sheet-pinned-cell")
);

// A host passing a key it has no value for means unstated, not off. The
// pointer has to say coarse for the two to tell apart.
var realMM = window.matchMedia;
window.matchMedia = function (query) {
return query === "(pointer: coarse)"
? { matches: true }
: realMM.call(window, query);
};
odr.editing.setSheetOptions({ editOnClick: undefined });
check("a key passed unset asks the pointer rather than reading as off", tapEditsNow());
window.matchMedia = realMM;

var threw = false;
try {
odr.editing.setSheetOptions({ notAnOption: true });
} catch (error) {
threw = true;
}
check("an unknown option throws", threw);

odr.editing.setSheetOptions({ editOnClick: null });
odr.editing.disable();
odr.editing.enable();

odr.editing.editAt(3, 0);
editor().value = "one";
press(editor(), "Enter");
Expand Down
Loading