diff --git a/css/80_app.css b/css/80_app.css index 31442daec0..fa1ce025d3 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -5249,17 +5249,16 @@ button.conflicts-button { } .tooltip-keyhint { - padding: 10px; - margin: 10px -10px -10px -10px; + padding: 10px; + margin: 10px -10px -10px -10px; } -.popover-inner .shortcut { - font-weight: bold; - margin-left: 5px; +.tooltip-keyhint .tooltip-keys { + padding: 0 3px; + white-space: nowrap; } - -.ideditor[dir='rtl'] .popover-inner .shortcut { - margin-left: 0; - margin-right: 5px; +.tooltip-keys kbd.shortcut { + font-size: 10px; + font-weight: bold; } /* dark tooltips for sidebar / panels */ diff --git a/data/core.yaml b/data/core.yaml index 74367f3c9f..9f1dc2ca70 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -556,7 +556,7 @@ en: title: Redo tooltip: "Redo: {action}" nothing: Nothing to redo. - tooltip_keyhint: "Shortcut:" + tooltip_keyhint: "Key:" translate: translate: Add multilingual name localized_translation_label: Multilingual Name diff --git a/modules/ui/edit_menu.js b/modules/ui/edit_menu.js index 870464c524..0fd9d0ca48 100644 --- a/modules/ui/edit_menu.js +++ b/modules/ui/edit_menu.js @@ -97,7 +97,7 @@ export function uiEditMenu(context) { let tooltip = uiTooltip(context) .heading(d.title) .title(d.tooltip()) - .keys([d.keys[0]]); // display the first key combo, if there are alternates + .shortcut(d.keys[0]); // display the first key combo, if there are alternates _tooltips.push(tooltip); diff --git a/modules/ui/pane.js b/modules/ui/pane.js index 548c639e8a..f2a3816b03 100644 --- a/modules/ui/pane.js +++ b/modules/ui/pane.js @@ -76,7 +76,7 @@ export function uiPane(context, id) { _paneTooltip = uiTooltip(context) .placement(isRTL ? 'right' : 'left') .title(_description) - .keys([_key]); + .shortcut(_key); } selection diff --git a/modules/ui/panes/help.js b/modules/ui/panes/help.js index ec28d3a4bc..6eeac0c141 100644 --- a/modules/ui/panes/help.js +++ b/modules/ui/panes/help.js @@ -378,7 +378,7 @@ export function uiPaneHelp(context) { .attr('class', 'shortcuts') .call(uiTooltip(context) .title(l10n.t('shortcuts.tooltip')) - .keys(['?']) + .shortcut('?') .placement('top') ) .append('a') diff --git a/modules/ui/rapid_feature_inspector.js b/modules/ui/rapid_feature_inspector.js index 0d4946d598..234de89fe8 100644 --- a/modules/ui/rapid_feature_inspector.js +++ b/modules/ui/rapid_feature_inspector.js @@ -283,23 +283,23 @@ export function uiRapidFeatureInspector(context, keybinding) { .on('click', onClick); // build tooltips - let title, keys; + let title, shortcut; if (d.key === 'accept') { if (isAddFeatureDisabled()) { title = l10n.t('rapid_feature_inspector.option_accept.disabled', { n: ACCEPT_FEATURES_LIMIT } ); - keys = []; + shortcut = ''; } else { title = l10n.t('rapid_feature_inspector.option_accept.tooltip'); - keys = [l10n.t('rapid_feature_inspector.option_accept.key')]; + shortcut = l10n.t('rapid_feature_inspector.option_accept.key'); } } else if (d.key === 'ignore') { title = l10n.t('rapid_feature_inspector.option_ignore.tooltip'); - keys = [l10n.t('rapid_feature_inspector.option_ignore.key')]; + shortcut = l10n.t('rapid_feature_inspector.option_ignore.key'); } - if (title && keys) { + if (title) { choiceButton = choiceButton - .call(uiTooltip(context).placement('bottom').title(title).keys(keys)); + .call(uiTooltip(context).placement('bottom').title(title).shortcut(shortcut)); } choiceButton diff --git a/modules/ui/sections/background_list.js b/modules/ui/sections/background_list.js index 67413afc97..9f22b5f3a9 100644 --- a/modules/ui/sections/background_list.js +++ b/modules/ui/sections/background_list.js @@ -67,7 +67,7 @@ export function uiSectionBackgroundList(context) { .append('label') .call(uiTooltip(context) .title(l10n.t('background.minimap.tooltip')) - .keys([l10n.t('background.minimap.key')]) + .shortcut(l10n.t('background.minimap.key')) .placement('top') ); @@ -90,7 +90,7 @@ export function uiSectionBackgroundList(context) { .append('label') .call(uiTooltip(context) .title(l10n.t('background.3dmap.tooltip')) - .keys([uiCmd('⌘' + l10n.t('background.3dmap.key'))]) + .shortcut(uiCmd('⌘' + l10n.t('background.3dmap.key'))) .placement('top') ); @@ -113,7 +113,7 @@ export function uiSectionBackgroundList(context) { .append('label') .call(uiTooltip(context) .title(l10n.t('background.panel.tooltip')) - .keys([uiCmd('⌘⇧' + l10n.t('info_panels.background.key'))]) + .shortcut(uiCmd('⌘⇧' + l10n.t('info_panels.background.key'))) .placement('top') ); @@ -135,7 +135,7 @@ export function uiSectionBackgroundList(context) { .append('label') .call(uiTooltip(context) .title(l10n.t('background.location_panel.tooltip')) - .keys([uiCmd('⌘⇧' + l10n.t('info_panels.location.key'))]) + .shortcut(uiCmd('⌘⇧' + l10n.t('info_panels.location.key'))) .placement('top') ); @@ -183,7 +183,7 @@ export function uiSectionBackgroundList(context) { item.call(uiTooltip(context) .placement(placement) .title(l10n.t('background.switch')) - .keys([uiCmd('⌘' + l10n.t('background.key'))]) + .shortcut(uiCmd('⌘' + l10n.t('background.key'))) ); } else if (d.description || isOverflowing) { item.call(uiTooltip(context) diff --git a/modules/ui/sections/data_layers.js b/modules/ui/sections/data_layers.js index 72e5b3d65e..7cc49e4e58 100644 --- a/modules/ui/sections/data_layers.js +++ b/modules/ui/sections/data_layers.js @@ -91,7 +91,7 @@ export function uiSectionDataLayers(context) { d3_select(nodes[i]) .call(uiTooltip(context) .title(l10n.t(`map_data.layers.${d.id}.tooltip`)) - .keys([uiCmd('⌥' + l10n.t('area_fill.wireframe.key'))]) + .shortcut(uiCmd('⌥' + l10n.t('area_fill.wireframe.key'))) .placement('bottom') ); } else { @@ -286,7 +286,7 @@ export function uiSectionDataLayers(context) { .append('label') .call(uiTooltip(context) .title(l10n.t('map_data.history_panel.tooltip')) - .keys([uiCmd('⌘⇧' + l10n.t('info_panels.history.key'))]) + .shortcut(uiCmd('⌘⇧' + l10n.t('info_panels.history.key'))) .placement('top') ); @@ -308,7 +308,7 @@ export function uiSectionDataLayers(context) { .append('label') .call(uiTooltip(context) .title(l10n.t('map_data.measurement_panel.tooltip')) - .keys([uiCmd('⌘⇧' + l10n.t('info_panels.measurement.key'))]) + .shortcut(uiCmd('⌘⇧' + l10n.t('info_panels.measurement.key'))) .placement('top') ); diff --git a/modules/ui/sections/map_style_options.js b/modules/ui/sections/map_style_options.js index 33d8124ee5..7d72f96d35 100644 --- a/modules/ui/sections/map_style_options.js +++ b/modules/ui/sections/map_style_options.js @@ -44,12 +44,10 @@ export function uiSectionMapStyleOptions(context) { .append('li') .call(uiTooltip(context) .title(d => l10n.t(`${name}.${d}.tooltip`)) - .keys(d => { - let key = (d === 'wireframe' ? l10n.t('area_fill.wireframe.key') : null); - if (d === 'highlight_edits') { - key = l10n.t('map_data.highlight_edits.key'); - } - return key ? [key] : null; + .shortcut(d => { + if (d === 'wireframe') return l10n.t('area_fill.wireframe.key'); + if (d === 'highlight_edits') return l10n.t('map_data.highlight_edits.key'); + return null; }) .placement('top') ); diff --git a/modules/ui/tools/modes.js b/modules/ui/tools/modes.js index f05b008c33..eb9906f87e 100644 --- a/modules/ui/tools/modes.js +++ b/modules/ui/tools/modes.js @@ -77,7 +77,7 @@ export function uiToolDrawModes(context) { .call(uiTooltip(context) .placement('bottom') .title(d => d.description) - .keys(d => [d.key]) + .shortcut(d => d.key) .scrollContainer(context.container().select('.top-toolbar')) ); diff --git a/modules/ui/tools/notes.js b/modules/ui/tools/notes.js index abdec864d3..3ec011950a 100644 --- a/modules/ui/tools/notes.js +++ b/modules/ui/tools/notes.js @@ -67,7 +67,7 @@ export function uiToolNotes(context) { .call(uiTooltip(context) .placement('bottom') .title(d => d.description) - .keys(d => [d.key]) + .shortcut(d => d.key) .scrollContainer(context.container().select('.top-toolbar')) ); diff --git a/modules/ui/tools/rapid_features.js b/modules/ui/tools/rapid_features.js index 2d0be43900..bd15904043 100644 --- a/modules/ui/tools/rapid_features.js +++ b/modules/ui/tools/rapid_features.js @@ -66,7 +66,7 @@ export function uiToolRapidFeatures(context) { .call(uiTooltip(context) .placement('bottom') .title(l10n.t('shortcuts.browsing.display_options.rapid_features_data')) - .keys(rapidFeaturesToggleKey) + .shortcut(rapidFeaturesToggleKey) ); rapidButtonEnter diff --git a/modules/ui/tools/save.js b/modules/ui/tools/save.js index 46c41bf8d4..d3182ee475 100644 --- a/modules/ui/tools/save.js +++ b/modules/ui/tools/save.js @@ -58,7 +58,7 @@ export function uiToolSave(context) { if (_tooltip) { _tooltip .title(l10n.t(_numChanges > 0 ? 'save.help' : 'save.no_changes')) - .keys([key]); + .shortcut(key); } _button @@ -86,7 +86,7 @@ export function uiToolSave(context) { _tooltip = uiTooltip(context) .placement('bottom') .title(l10n.t('save.no_changes')) - .keys([key]) + .shortcut(key) .scrollContainer(context.container().select('.top-toolbar')); // var lastPointerUpType; diff --git a/modules/ui/tools/sidebar_toggle.js b/modules/ui/tools/sidebar_toggle.js index 158639030c..8756ebbac1 100644 --- a/modules/ui/tools/sidebar_toggle.js +++ b/modules/ui/tools/sidebar_toggle.js @@ -19,7 +19,7 @@ export function uiToolSidebarToggle(context) { .call(uiTooltip(context) .placement('bottom') .title(l10n.t('sidebar.tooltip')) - .keys([ l10n.t('sidebar.key') ]) + .shortcut(l10n.t('sidebar.key')) .scrollContainer(context.container().select('.top-toolbar')) ) .call(uiIcon('#rapid-icon-sidebar-' + (l10n.isRTL() ? 'right' : 'left'))); diff --git a/modules/ui/tools/undo_redo.js b/modules/ui/tools/undo_redo.js index d592b30607..6212b2b207 100644 --- a/modules/ui/tools/undo_redo.js +++ b/modules/ui/tools/undo_redo.js @@ -65,7 +65,7 @@ export function uiToolUndoRedo(context) { } return str ? l10n.t(`${d.id}.tooltip`, { action: str }) : l10n.t(`${d.id}.nothing`); }) - .keys(d => [d.key]) + .shortcut(d => d.key) .scrollContainer(context.container().select('.top-toolbar')); // var lastPointerUpType; diff --git a/modules/ui/tooltip.js b/modules/ui/tooltip.js index 20f3c521e6..54f89fd36a 100644 --- a/modules/ui/tooltip.js +++ b/modules/ui/tooltip.js @@ -6,110 +6,113 @@ import { uiCmd } from './cmd'; export function uiTooltip(context) { - const l10n = context.systems.l10n; - var tooltip = uiPopover(context, 'tooltip').displayType('hover'); - - var _title = function() { - var title = this.getAttribute('data-original-title'); - if (title) { - return title; - } else { - title = this.getAttribute('title'); - this.removeAttribute('title'); - this.setAttribute('data-original-title', title); - } - return title; - }; - - var _heading = utilFunctor(null); - var _keys = utilFunctor(null); - - tooltip.title = function(val) { - if (!arguments.length) return _title; - _title = utilFunctor(val); - return tooltip; - }; + const l10n = context.systems.l10n; + const tooltip = uiPopover(context, 'tooltip').displayType('hover'); + + let _title = function() { + var title = this.getAttribute('data-original-title'); + if (title) { + return title; + } else { + title = this.getAttribute('title'); + this.removeAttribute('title'); + this.setAttribute('data-original-title', title); + } + return title; + }; + + let _heading = utilFunctor(null); + let _shortcut = utilFunctor(null); + + tooltip.title = function(val) { + if (!arguments.length) return _title; + _title = utilFunctor(val); + return tooltip; + }; - tooltip.heading = function(val) { - if (!arguments.length) return _heading; - _heading = utilFunctor(val); - return tooltip; - }; + tooltip.heading = function(val) { + if (!arguments.length) return _heading; + _heading = utilFunctor(val); + return tooltip; + }; - tooltip.keys = function(val) { - if (!arguments.length) return _keys; - _keys = utilFunctor(val); - return tooltip; + tooltip.shortcut = function(val) { + if (!arguments.length) return _shortcut; + _shortcut = utilFunctor(val); + return tooltip; + }; + + + tooltip.content(function() { + const heading = _heading.apply(this, arguments); + const text = _title.apply(this, arguments); + const shortcut = _shortcut.apply(this, arguments); + + return function(selection) { + const headingWrap = selection + .selectAll('.tooltip-heading') + .data(heading ? [heading] : []); + + headingWrap.exit() + .remove(); + + headingWrap.enter() + .append('div') + .attr('class', 'tooltip-heading') + .merge(headingWrap) + .text(d => d); + + const textWrap = selection + .selectAll('.tooltip-text') + .data(text ? [text] : []); + + textWrap.exit() + .remove(); + + textWrap.enter() + .append('div') + .attr('class', 'tooltip-text') + .merge(textWrap) + .html(d => d); // watch out: a few tooltips still send html through here + + const shortcutWrap = selection + .selectAll('.tooltip-keyhint') + .data(shortcut ? [shortcut] : []); + + shortcutWrap.exit() + .remove(); + + const shortcutWrapEnter = shortcutWrap.enter() + .append('div') + .attr('class', 'tooltip-keyhint') + .text(d => d.length === 1 ? l10n.t('tooltip_keyhint') : null); // "Shortcut:" + + const shortcutKeysEnter = shortcutWrapEnter + .append('span') + .attr('class', 'tooltip-keys'); + + // Split the shortcut string into an array and display a `kbd` for each one + // Warning: this will fail if the key is multiple character like 'F11' + // (we aren't displaying this in a tooltip currently) + shortcutKeysEnter.selectAll('kbd.shortcut') + .data(d => (typeof d === 'string') ? d.split('') : []) + .enter() + .each((d, i, nodes) => { + const selection = d3_select(nodes[i]); + + selection + .append('kbd') + .attr('class', 'shortcut') + .text(d => uiCmd.display(context, d)); + + if (i < shortcut.length - 1) { + selection + .append('span') + .text('+'); + } + }); }; + }); - tooltip.content(function() { - var heading = _heading.apply(this, arguments); - var text = _title.apply(this, arguments); - var keys = _keys.apply(this, arguments); - - return function(selection) { - var headingSelect = selection - .selectAll('.tooltip-heading') - .data(heading ? [heading] :[]); - - headingSelect.exit() - .remove(); - - headingSelect.enter() - .append('div') - .attr('class', 'tooltip-heading') - .merge(headingSelect) - .text(heading); - - var textSelect = selection - .selectAll('.tooltip-text') - .data(text ? [text] :[]); - - textSelect.exit() - .remove(); - - textSelect.enter() - .append('div') - .attr('class', 'tooltip-text') - .merge(textSelect) - .html(text); // watch out: a few tooltips still send html through here - - var keyhintWrap = selection - .selectAll('.tooltip-keyhint') - .data(keys?.length ? [0] : []); - - keyhintWrap.exit() - .remove(); - - var keyhintWrapEnter = keyhintWrap.enter() - .append('div') - .attr('class', 'tooltip-keyhint'); - - keyhintWrapEnter - .append('span') - .text(l10n.t('tooltip_keyhint')); // "Shortcut:" - - keyhintWrap = keyhintWrapEnter.merge(keyhintWrap); - - keyhintWrap.selectAll('kbd.shortcut') - .data(keys?.length ? keys : []) - .enter() - .each((d, i, nodes) => { - const selection = d3_select(nodes[i]); - - selection - .append('kbd') - .attr('class', 'shortcut') - .text(d => uiCmd.display(context, d)); - - if (i < keys.length - 1) { - selection - .append('span') - .text('+'); - } - }); - }; - }); - - return tooltip; + return tooltip; } diff --git a/modules/ui/zoom.js b/modules/ui/zoom.js index 64f9013da2..8baa52613b 100644 --- a/modules/ui/zoom.js +++ b/modules/ui/zoom.js @@ -53,7 +53,7 @@ export function uiZoom(context) { let tooltipBehavior = uiTooltip(context) .placement(l10n.isRTL() ? 'right' : 'left') .title(d => d.isDisabled() ? d.disabledTitle : d.title) - .keys(d => [d.key]); + .shortcut(d => d.key); let _lastPointerUpType; diff --git a/modules/ui/zoom_to_selection.js b/modules/ui/zoom_to_selection.js index 38d0a3e814..b93c1e8a6d 100644 --- a/modules/ui/zoom_to_selection.js +++ b/modules/ui/zoom_to_selection.js @@ -16,7 +16,7 @@ export function uiZoomToSelection(context) { let tooltip = uiTooltip(context) .placement(l10n.isRTL() ? 'right' : 'left') .title(() => isDisabled() ? l10n.t('inspector.zoom_to.no_selection') : l10n.t('inspector.zoom_to.title')) - .keys([shortcutKey]); + .shortcut(shortcutKey); let button = selection .append('button')