diff --git a/css/80_app.css b/css/80_app.css index 801158597..c2a9b5213 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3703,6 +3703,35 @@ img.tile-removing { margin-right: -20px; } +.curtain-tooltip.intro-mouse { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.curtain-tooltip.intro-mouse .counter { + position: absolute; + display: block; + top: 50px; + width: 100%; + text-align: center; + font-weight: bold; + font-size: 14px; + z-index: 1003; +} + +.curtain-tooltip.intro-mouse .tooltip-illustration use { + fill: rgba(112, 146, 255, 0); + color: rgba(112, 146, 255, 0); +} +.curtain-tooltip.intro-mouse.leftclick .tooltip-illustration use { + fill: rgba(112, 146, 255, 1); +} +.curtain-tooltip.intro-mouse.rightclick .tooltip-illustration use { + color: rgba(112, 146, 255, 1); +} + .huge-modal-button { width: 100%; height: auto; diff --git a/data/core.yaml b/data/core.yaml index 37a8aa9d8..e454fe58a 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -897,7 +897,10 @@ en: welcome: "Welcome! This walkthrough will teach you the basics of editing on OpenStreetMap." practice: "All of the data in this walkthrough is just for practicing, and any edits that you make in the walkthrough will not be saved." words: "This walkthrough will introduce some new words and concepts. When we introduce a new word, we'll use *italics*." - chapters: "You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**" + mouse: "You can use any input device to edit the map, but this walkthrough assumes you have a mouse with left and right buttons. **If you want to attach a mouse, do so now, then click OK.**" + leftclick: "When this tutorial asks you to click or double-click, we mean with the left button. On a trackpad it might be a single-click or single-finger tap. **Left-click {num} times.**" + rightclick: "Sometimes we'll also ask you to right-click. This might be the same as control-click, or two-finger tap on a trackpad. Your keyboard might even have a 'menu' key that works like right-click. **Right-click {num} times.**" + chapters: "So far, so good! You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**" navigation: title: "Navigation" drag: "The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**" diff --git a/dist/locales/en.json b/dist/locales/en.json index 90e7adcb5..32d8a50ee 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -756,7 +756,10 @@ "welcome": "Welcome! This walkthrough will teach you the basics of editing on OpenStreetMap.", "practice": "All of the data in this walkthrough is just for practicing, and any edits that you make in the walkthrough will not be saved.", "words": "This walkthrough will introduce some new words and concepts. When we introduce a new word, we'll use *italics*.", - "chapters": "You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**" + "mouse": "You can use any input device to edit the map, but this walkthrough assumes you have a mouse with left and right buttons. **If you want to attach a mouse, do so now, then click OK.**", + "leftclick": "When this tutorial asks you to click or double-click, we mean with the left button. On a trackpad it might be a single-click or single-finger tap. **Left-click {num} times.**", + "rightclick": "Sometimes we'll also ask you to right-click. This might be the same as control-click, or two-finger tap on a trackpad. Your keyboard might even have a 'menu' key that works like right-click. **Right-click {num} times.**", + "chapters": "So far, so good! You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**" }, "navigation": { "title": "Navigation", diff --git a/modules/ui/curtain.js b/modules/ui/curtain.js index 4d947334e..267021994 100644 --- a/modules/ui/curtain.js +++ b/modules/ui/curtain.js @@ -126,6 +126,13 @@ export function uiCurtain() { tooltipArrow = 5, side, pos; + + // hack: this will have bottom placement, + // so need to reserve extra space for the tooltip illustration. + if (options.tooltipClass === 'intro-mouse') { + tip.height += 80; + } + // trim box dimensions to just the portion that fits in the window.. if (tooltipBox.top + tooltipBox.height > h) { tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h); diff --git a/modules/ui/intro/welcome.js b/modules/ui/intro/welcome.js index 8af0a59d8..155d2555c 100644 --- a/modules/ui/intro/welcome.js +++ b/modules/ui/intro/welcome.js @@ -4,8 +4,8 @@ import { utilRebind } from '../../util/rebind'; export function uiIntroWelcome(context, reveal) { - var dispatch = d3.dispatch('done'); - + var dispatch = d3.dispatch('done'), + listener = clickListener(); var chapter = { title: 'intro.welcome.title' @@ -30,10 +30,101 @@ export function uiIntroWelcome(context, reveal) { function words() { reveal('.intro-nav-wrap .chapter-welcome', t('intro.welcome.words'), - { buttonText: t('intro.ok'), buttonCallback: chapters } + { buttonText: t('intro.ok'), buttonCallback: mouse } ); } + + function mouse() { + reveal('.intro-nav-wrap .chapter-welcome', + t('intro.welcome.mouse'), + { buttonText: t('intro.ok'), buttonCallback: leftClick } + ); + } + + + function leftClick() { + var counter = 0, + times = 5; + + var tooltip = reveal('.intro-nav-wrap .chapter-welcome', + t('intro.welcome.leftclick', { num: times }), + { tooltipClass: 'intro-mouse' } + ); + + tooltip.selectAll('.tooltip-inner') + .insert('svg', 'span') + .attr('class', 'tooltip-illustration') + .append('use') + .attr('xlink:href', '#walkthrough-mouse'); + + tooltip + .append('div') + .attr('class', 'counter'); + + tooltip.call(listener); + + listener.on('click', function(which) { + if (which === 'left') { + d3.select('.curtain-tooltip.intro-mouse .counter') + .text(String(++counter)); + + if (counter === times) { + window.setTimeout(function() { continueTo(rightClick); }, 1000); + } + } + }); + + function continueTo(nextStep) { + listener.on('click', null); + tooltip.call(listener.off); + tooltip.select('.counter').remove(); + nextStep(); + } + } + + + function rightClick() { + var counter = 0, + times = 5; + + var tooltip = reveal('.intro-nav-wrap .chapter-welcome', + t('intro.welcome.rightclick', { num: times }), + { tooltipClass: 'intro-mouse' } + ); + + tooltip.selectAll('.tooltip-inner') + .insert('svg', 'span') + .attr('class', 'tooltip-illustration') + .append('use') + .attr('xlink:href', '#walkthrough-mouse'); + + tooltip + .append('div') + .attr('class', 'counter'); + + tooltip.call(listener); + + listener.on('click', function(which) { + if (which === 'right') { + d3.select('.curtain-tooltip.intro-mouse .counter') + .text(String(++counter)); + + if (counter === times) { + window.setTimeout(function() { continueTo(chapters); }, 1000); + } + } + }); + + function continueTo(nextStep) { + listener.on('click', null); + tooltip.call(listener.off); + tooltip.select('.counter').remove(); + nextStep(); + } + } + + function chapters() { dispatch.call('done'); reveal('.intro-nav-wrap .chapter-navigation', @@ -48,6 +139,7 @@ export function uiIntroWelcome(context, reveal) { chapter.exit = function() { + listener.off(); }; @@ -59,3 +151,104 @@ export function uiIntroWelcome(context, reveal) { return utilRebind(chapter, dispatch, 'on'); } + + + +function clickListener() { + var dispatch = d3.dispatch('click'), + minTime = 120, + tooltip = d3.select(null), + down = {}; + + + function keydown() { + if (d3.event.keyCode === 93) { //context menu + d3.event.preventDefault(); + d3.event.stopPropagation(); + down.menu = d3.event.timeStamp; + tooltip.classed('rightclick', true); + } + } + + + function keyup() { + if (d3.event.keyCode === 93) { //context menu + d3.event.preventDefault(); + d3.event.stopPropagation(); + var endTime = d3.event.timeStamp, + startTime = down.menu || endTime, + delay = (endTime - startTime < minTime) ? minTime : 0; + + window.setTimeout(function() { tooltip.classed('rightclick', false); }, delay); + dispatch.call('click', this, 'right'); + down.menu = undefined; + } + } + + + function mousedown() { + if (d3.event.button === 0 && !d3.event.ctrlKey) { + tooltip.classed('leftclick', true); + } else if (d3.event.button === 2) { + tooltip.classed('rightclick', true); + } + down[d3.event.button] = d3.event.timeStamp; + } + + + function mouseup() { + var endTime = d3.event.timeStamp, + startTime = down[d3.event.button] || endTime, + delay = (endTime - startTime < minTime) ? minTime : 0; + + if (d3.event.button === 0 && !d3.event.ctrlKey) { + window.setTimeout(function() { tooltip.classed('leftclick', false); }, delay); + dispatch.call('click', this, 'left'); + } else if (d3.event.button === 2) { + window.setTimeout(function() { tooltip.classed('rightclick', false); }, delay); + dispatch.call('click', this, 'right'); + } + down[d3.event.button] = undefined; + } + + + function contextmenu() { + d3.event.preventDefault(); + d3.event.stopPropagation(); + if (!down[2] && !down.menu) { + tooltip.classed('rightclick', true); + window.setTimeout(function() { tooltip.classed('rightclick', false); }, minTime); + dispatch.call('click', this, 'right'); + } + } + + + var behavior = function(selection) { + tooltip = selection; + down = {}; + + d3.select(window) + .on('keydown.intro', keydown) + .on('keyup.intro', keyup) + .on('mousedown.intro', mousedown) + .on('mouseup.intro', mouseup) + .on('contextmenu.intro', contextmenu); + }; + + + behavior.off = function() { + d3.select(window) + .on('keydown.intro', null) + .on('keyup.intro', null) + .on('mousedown.intro', null) + .on('mouseup.intro', null) + .on('contextmenu.intro', null); + + tooltip + .classed('leftclick', false) + .classed('rightclick', false); + }; + + return utilRebind(behavior, dispatch, 'on'); +} + diff --git a/svg/iD-sprite.json b/svg/iD-sprite.json index 41699554c..3047f98d2 100644 --- a/svg/iD-sprite.json +++ b/svg/iD-sprite.json @@ -402,5 +402,11 @@ "poi-images": { "viewBox": "0 320 200 80" }, "landuse-images": { "viewBox": "0 400 200 80" }, "feature-images": { "viewBox": "0 480 200 80" }, - "building-images": { "viewBox": "700 480 200 80" } + "building-images": { "viewBox": "700 480 200 80" }, + + "walkthrough-mouse": { "viewBox": "400 411 25 43" }, + + "walkthrough-mouse-shape": { "fill": "#000000" }, + "walkthrough-mouse-left": { "fill": "inherit" }, + "walkthrough-mouse-right": { "fill": "currentColor" } } diff --git a/svg/iD-sprite.src.idraw b/svg/iD-sprite.src.idraw index 42ef70b4a..7b9ebd908 100644 Binary files a/svg/iD-sprite.src.idraw and b/svg/iD-sprite.src.idraw differ diff --git a/svg/iD-sprite.src.svg b/svg/iD-sprite.src.svg index 76802fef0..6021c0325 100644 --- a/svg/iD-sprite.src.svg +++ b/svg/iD-sprite.src.svg @@ -194,6 +194,11 @@ + + + + +