blob: 20d093e31e45ddbafa9b3ca04c684b2b6a738795 [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gwtexpui.globalkey.client;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class KeyHelpPopup extends PopupPanel implements KeyPressHandler, KeyDownHandler {
private final FocusPanel focus;
public KeyHelpPopup() {
super(true /* autohide */, true /* modal */);
setStyleName(KeyResources.I.css().helpPopup());
final Anchor closer = new Anchor(KeyConstants.I.closeButton());
closer.addClickHandler(
new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
hide();
}
});
final Grid header = new Grid(1, 3);
header.setStyleName(KeyResources.I.css().helpHeader());
header.setText(0, 0, KeyConstants.I.keyboardShortcuts());
header.setWidget(0, 2, closer);
final CellFormatter fmt = header.getCellFormatter();
fmt.addStyleName(0, 1, KeyResources.I.css().helpHeaderGlue());
fmt.setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_RIGHT);
final Grid lists = new Grid(0, 7);
lists.setStyleName(KeyResources.I.css().helpTable());
populate(lists);
lists.getCellFormatter().addStyleName(0, 3, KeyResources.I.css().helpTableGlue());
final FlowPanel body = new FlowPanel();
body.add(header);
body.getElement().appendChild(DOM.createElement("hr"));
body.add(lists);
focus = new FocusPanel(body);
focus.getElement().getStyle().setProperty("outline", "0px");
focus.getElement().setAttribute("hideFocus", "true");
focus.addKeyPressHandler(this);
focus.addKeyDownHandler(this);
add(focus);
}
@Override
public void setVisible(boolean show) {
super.setVisible(show);
if (show) {
focus.setFocus(true);
}
}
@Override
public void onKeyPress(KeyPressEvent event) {
if (KeyCommandSet.toMask(event) == ShowHelpCommand.INSTANCE.keyMask) {
// Block the '?' key from triggering us to show right after
// we just hide ourselves.
//
event.stopPropagation();
event.preventDefault();
}
hide();
}
@Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
hide();
}
}
private void populate(Grid lists) {
int[] end = new int[5];
int column = 0;
for (KeyCommandSet set : combinedSetsByName()) {
int row = end[column];
row = formatGroup(lists, row, column, set);
end[column] = row;
if (column == 0) {
column = 4;
} else {
column = 0;
}
}
}
/**
* @return an ordered collection of KeyCommandSet, combining sets which share the same name, so
* that each set name appears at most once.
*/
private static Collection<KeyCommandSet> combinedSetsByName() {
LinkedHashMap<String, KeyCommandSet> byName = new LinkedHashMap<>();
for (KeyCommandSet set : GlobalKey.active.all.getSets()) {
KeyCommandSet v = byName.get(set.getName());
if (v == null) {
v = new KeyCommandSet(set.getName());
byName.put(v.getName(), v);
}
v.add(set);
}
return byName.values();
}
private int formatGroup(Grid lists, int row, int col, KeyCommandSet set) {
if (set.isEmpty()) {
return row;
}
if (lists.getRowCount() < row + 1) {
lists.resizeRows(row + 1);
}
lists.setText(row, col + 2, set.getName());
lists.getCellFormatter().addStyleName(row, col + 2, KeyResources.I.css().helpGroup());
row++;
return formatKeys(lists, row, col, set, null);
}
private int formatKeys(final Grid lists, int row, int col, KeyCommandSet set, SafeHtml prefix) {
final CellFormatter fmt = lists.getCellFormatter();
final List<KeyCommand> keys = sort(set);
if (lists.getRowCount() < row + keys.size()) {
lists.resizeRows(row + keys.size());
}
Map<KeyCommand, Integer> rows = new HashMap<>();
FORMAT_KEYS:
for (int i = 0; i < keys.size(); i++) {
final KeyCommand k = keys.get(i);
if (rows.containsKey(k)) {
continue;
}
if (k instanceof CompoundKeyCommand) {
final SafeHtmlBuilder b = new SafeHtmlBuilder();
b.append(k.describeKeyStroke());
row = formatKeys(lists, row, col, ((CompoundKeyCommand) k).getSet(), b);
continue;
}
for (int prior = 0; prior < i; prior++) {
if (KeyCommand.same(keys.get(prior), k)) {
final int r = rows.get(keys.get(prior));
final SafeHtmlBuilder b = new SafeHtmlBuilder();
b.append(SafeHtml.get(lists, r, col + 0));
b.append(" ");
b.append(KeyConstants.I.orOtherKey());
b.append(" ");
if (prefix != null) {
b.append(prefix);
b.append(" ");
b.append(KeyConstants.I.thenOtherKey());
b.append(" ");
}
b.append(k.describeKeyStroke());
SafeHtml.set(lists, r, col + 0, b);
rows.put(k, r);
continue FORMAT_KEYS;
}
}
SafeHtmlBuilder b = new SafeHtmlBuilder();
String t = k.getHelpText();
if (prefix != null) {
b.append(prefix);
b.append(" ");
b.append(KeyConstants.I.thenOtherKey());
b.append(" ");
}
b.append(k.describeKeyStroke());
if (k.sibling != null) {
b.append(" / ").append(k.sibling.describeKeyStroke());
t += " / " + k.sibling.getHelpText();
rows.put(k.sibling, row);
}
SafeHtml.set(lists, row, col + 0, b);
lists.setText(row, col + 1, ":");
lists.setText(row, col + 2, t);
rows.put(k, row);
fmt.addStyleName(row, col + 0, KeyResources.I.css().helpKeyStroke());
fmt.addStyleName(row, col + 1, KeyResources.I.css().helpSeparator());
row++;
}
return row;
}
private List<KeyCommand> sort(KeyCommandSet set) {
return set.getKeys().stream().sorted(comparing(KeyCommand::getHelpText)).collect(toList());
}
}