diff options
Diffstat (limited to 'java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java')
-rw-r--r-- | java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java new file mode 100644 index 0000000000..20d093e31e --- /dev/null +++ b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java @@ -0,0 +1,233 @@ +// 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()); + } +} |