Index: trunk/extensions/VisualEditor/modules/es/views/es.SurfaceView.js |
— | — | @@ -140,29 +140,41 @@ |
141 | 141 | |
142 | 142 | es.SurfaceView.prototype.onMouseDown = function( e ) { |
143 | 143 | if ( e.button === 0 ) { // left mouse button |
| 144 | + |
144 | 145 | var offset = this.documentView.getOffsetFromEvent( e ); |
| 146 | + |
| 147 | + console.log('onMouseDown; offset: ' + offset); |
| 148 | + |
145 | 149 | if ( e.originalEvent.detail === 1 ) { // single click |
146 | 150 | this.mouse.selectingMode = 1; // used in mouseMove handler |
| 151 | + |
147 | 152 | if ( this.keyboard.keys.shift && offset !== this.selection.from ) { |
| 153 | + // extend current or create new selection |
148 | 154 | this.selection.to = offset; |
149 | 155 | } else { |
150 | 156 | if ( this.selection.to !== this.selection.from ) { |
| 157 | + // clear the selection if there was any |
151 | 158 | this.documentView.clearSelection(); |
152 | 159 | } |
153 | 160 | this.selection.from = this.selection.to = offset; |
| 161 | + |
154 | 162 | var position = es.Position.newFromEventPagePosition( e ), |
155 | 163 | nodeView = this.documentView.getNodeFromOffset( offset, false ); |
156 | 164 | this.cursor.initialBias = position.left > nodeView.contentView.$.offset().left; |
157 | 165 | } |
| 166 | + |
158 | 167 | } else if ( e.originalEvent.detail === 2 ) { // double click |
159 | 168 | this.mouse.selectingMode = 2; // used in mouseMove handler |
| 169 | + |
160 | 170 | var wordRange = this.documentView.model.getWordBoundaries( offset ); |
161 | 171 | if( wordRange ) { |
162 | 172 | this.selection = wordRange; |
163 | 173 | this.mouse.selectedRange = this.selection.clone(); |
164 | 174 | } |
| 175 | + |
165 | 176 | } else if ( e.originalEvent.detail >= 3 ) { // triple click |
166 | 177 | this.mouse.selectingMode = 3; // used in mouseMove handler |
| 178 | + |
167 | 179 | var node = this.documentView.getNodeFromOffset( offset ); |
168 | 180 | this.selection.from = this.documentView.getOffsetFromNode( node, false ); |
169 | 181 | this.selection.to = this.selection.from + node.getElementLength() - 1; |
— | — | @@ -187,35 +199,37 @@ |
188 | 200 | |
189 | 201 | es.SurfaceView.prototype.onMouseMove = function( e ) { |
190 | 202 | if ( e.button === 0 && this.mouse.selectingMode ) { // left mouse button and in selecting mode |
| 203 | + |
191 | 204 | var offset = this.documentView.getOffsetFromEvent( e ); |
192 | | - if ( this.mouse.selectingMode === 1 ) { |
| 205 | + |
| 206 | + if ( this.mouse.selectingMode === 1 ) { // selection of chars |
193 | 207 | this.selection.to = offset; |
194 | | - } else if ( this.mouse.selectingMode === 2 ) { |
| 208 | + } else if ( this.mouse.selectingMode === 2 ) { // selection of words |
195 | 209 | var wordRange = this.documentView.model.getWordBoundaries( offset ); |
196 | 210 | if ( wordRange ) { |
197 | 211 | if ( wordRange.to <= this.mouse.selectedRange.from ) { |
198 | 212 | this.selection.to = wordRange.from; |
199 | 213 | this.selection.from = this.mouse.selectedRange.to; |
200 | 214 | } else { |
| 215 | + this.selection.to = wordRange.to; |
201 | 216 | this.selection.from = this.mouse.selectedRange.from; |
202 | | - this.selection.to = wordRange.to; |
203 | 217 | } |
204 | 218 | } else { |
205 | 219 | this.selection.to = offset; |
206 | 220 | } |
207 | 221 | } else if ( this.mouse.selectingMode === 3 ) { |
208 | | - var node = this.documentView.getNodeFromOffset( offset ); |
209 | | - var nodeRange = new es.Range(); |
210 | | - nodeRange.from = this.documentView.getOffsetFromNode( node, false ); |
211 | | - nodeRange.to = nodeRange.from + node.getElementLength() - 1; |
| 222 | + var nodeRange = this.documentView.getRangeFromNode( |
| 223 | + this.documentView.getNodeFromOffset( offset ) |
| 224 | + ); |
212 | 225 | if ( nodeRange.to <= this.mouse.selectedRange.from ) { |
213 | 226 | this.selection.to = nodeRange.from; |
214 | 227 | this.selection.from = this.mouse.selectedRange.to; |
215 | 228 | } else { |
| 229 | + this.selection.to = nodeRange.to; |
216 | 230 | this.selection.from = this.mouse.selectedRange.from; |
217 | | - this.selection.to = nodeRange.to; |
218 | 231 | } |
219 | 232 | } |
| 233 | + |
220 | 234 | this.documentView.drawSelection( this.selection ); |
221 | 235 | if ( this.selection.from !== this.selection.to ) { |
222 | 236 | this.hideCursor(); |
— | — | @@ -248,44 +262,47 @@ |
249 | 263 | this.keyboard.keys.command = true; |
250 | 264 | break; |
251 | 265 | case 36: // Home |
252 | | - this.moveCursor( 'home' ); |
| 266 | + this.moveCursor( 'left', 'line' ); |
253 | 267 | break; |
254 | 268 | case 35: // End |
255 | | - this.moveCursor( 'end' ); |
| 269 | + this.moveCursor( 'right', 'line' ); |
256 | 270 | break; |
257 | 271 | case 37: // Left arrow |
258 | 272 | if ( this.keyboard.keys.command ) { |
259 | | - this.moveCursor( 'home' ); |
260 | | - } else { |
261 | | - this.moveCursor( 'left' ); |
| 273 | + this.moveCursor( 'left', 'line' ); |
| 274 | + } else if ( this.keyboard.keys.control || this.keyboard.keys.alt ) { |
| 275 | + this.moveCursor( 'left', 'word' ); |
| 276 | + } else { |
| 277 | + this.moveCursor( 'left', 'char' ); |
262 | 278 | } |
263 | 279 | break; |
264 | 280 | case 38: // Up arrow |
265 | | - this.moveCursor( 'up' ); |
| 281 | + if ( this.keyboard.keys.control || this.keyboard.keys.alt ) { |
| 282 | + this.moveCursor( 'up', 'unit' ); |
| 283 | + } else { |
| 284 | + this.moveCursor( 'up', 'char' ); |
| 285 | + } |
| 286 | + |
266 | 287 | break; |
267 | 288 | case 39: // Right arrow |
268 | 289 | if ( this.keyboard.keys.command ) { |
269 | | - this.moveCursor( 'end' ); |
270 | | - } else { |
271 | | - this.moveCursor( 'right' ); |
| 290 | + this.moveCursor( 'right', 'line' ); |
| 291 | + } else if ( this.keyboard.keys.control || this.keyboard.keys.alt ) { |
| 292 | + this.moveCursor( 'right', 'word' ); |
| 293 | + } else { |
| 294 | + this.moveCursor( 'right', 'char' ); |
272 | 295 | } |
273 | 296 | break; |
274 | 297 | case 40: // Down arrow |
275 | | - this.moveCursor( 'down' ); |
| 298 | + if ( this.keyboard.keys.control || this.keyboard.keys.alt ) { |
| 299 | + this.moveCursor( 'down', 'unit' ); |
| 300 | + } else { |
| 301 | + this.moveCursor( 'down', 'char' ); |
| 302 | + } |
276 | 303 | break; |
277 | 304 | case 8: // Backspace |
278 | | - tx = this.documentView.model.prepareRemoval( |
279 | | - new es.Range( this.selection.to, this.selection.to - 1 ) |
280 | | - ); |
281 | | - this.documentView.model.commit( tx ); |
282 | | - this.selection.from = this.selection.to -= 1; |
283 | | - this.showCursor(); |
284 | 305 | break; |
285 | 306 | case 46: // Delete |
286 | | - tx = this.documentView.model.prepareRemoval( |
287 | | - new es.Range( this.selection.to, this.selection.to + 1 ) |
288 | | - ); |
289 | | - this.documentView.model.commit( tx ); |
290 | 307 | break; |
291 | 308 | default: // Insert content (maybe) |
292 | 309 | if ( this.keyboard.keydownTimeout ) { |
— | — | @@ -336,108 +353,109 @@ |
337 | 354 | return true; |
338 | 355 | }; |
339 | 356 | |
340 | | -es.SurfaceView.prototype.moveCursor = function( instruction ) { |
341 | | - this.selection.normalize(); |
| 357 | +/** |
| 358 | + * @param {String} direction up | down | left | right |
| 359 | + * @param {String} unit char | word | line | node | page |
| 360 | + */ |
| 361 | +es.SurfaceView.prototype.moveCursor = function( direction, unit ) { |
| 362 | + console.log('moveCursor; direction: ' + direction + ', unit: ' + unit); |
342 | 363 | |
343 | | - if ( instruction !== 'up' && instruction !== 'down' ) { |
| 364 | + if ( direction !== 'up' && direction !== 'down' ) { |
344 | 365 | this.cursor.initialLeft = null; |
345 | 366 | } |
| 367 | + |
| 368 | + this.selection.normalize(); |
346 | 369 | |
347 | | - var newTo; |
| 370 | + var to; |
348 | 371 | |
349 | | - switch ( instruction ) { |
350 | | - case 'left' : |
351 | | - case 'right' : |
352 | | - var offset; |
353 | | - if ( this.keyboard.keys.shift || this.selection.from === this.selection.to ) { |
354 | | - offset = this.selection.to; |
355 | | - } else { |
356 | | - offset = instruction === 'left' ? this.selection.start : this.selection.end; |
| 372 | + switch ( direction ) { |
| 373 | + case 'left': |
| 374 | + case 'right': |
| 375 | + switch ( unit ) { |
| 376 | + case 'char': |
| 377 | + case 'word': |
| 378 | + var offset; |
| 379 | + if ( this.keyboard.keys.shift || this.selection.from === this.selection.to ) { |
| 380 | + offset = this.selection.to; |
| 381 | + } else { |
| 382 | + offset = direction === 'left' ? this.selection.start : this.selection.end; |
| 383 | + } |
| 384 | + to = this.documentView.getModel().getRelativeContentOffset( |
| 385 | + offset, |
| 386 | + direction === 'left' ? -1 : 1 |
| 387 | + ); |
| 388 | + if ( unit === 'word' ) { |
| 389 | + var wordRange = this.documentView.model.getWordBoundaries( |
| 390 | + direction === 'left' ? to : offset |
| 391 | + ); |
| 392 | + if ( wordRange ) { |
| 393 | + to = direction === 'left' ? wordRange.start : wordRange.end; |
| 394 | + } |
| 395 | + } |
| 396 | + break; |
| 397 | + case 'line': |
| 398 | + var offset = this.cursor.initialBias ? |
| 399 | + this.documentView.getModel().getRelativeContentOffset( |
| 400 | + this.selection.to, |
| 401 | + -1) : |
| 402 | + this.selection.to; |
| 403 | + var range = this.documentView.getRenderedLineRangeFromOffset( offset ); |
| 404 | + to = direction === 'left' ? range.start : range.end; |
| 405 | + break; |
357 | 406 | } |
358 | | - newTo = this.documentView.getModel().getRelativeContentOffset( |
359 | | - offset, |
360 | | - instruction === 'left' ? -1 : 1 |
361 | | - ); |
362 | | - |
363 | | - if ( this.keyboard.keys.control || this.keyboard.keys.alt ) { |
364 | | - var wordRange = this.documentView.model.getWordBoundaries( |
365 | | - instruction === 'left' ? newTo : offset |
366 | | - ); |
367 | | - if ( wordRange ) { |
368 | | - newTo = instruction === 'left' ? wordRange.from : wordRange.to; |
369 | | - } |
370 | | - } |
371 | | - |
372 | 407 | break; |
373 | | - case 'home' : |
374 | | - case 'end' : |
375 | | - var range = this.documentView.getRenderedLineRangeFromOffset( |
376 | | - this.cursor.initialBias ? |
377 | | - this.documentView.getModel().getRelativeContentOffset( this.selection.to, -1 ) : |
| 408 | + case 'up': |
| 409 | + case 'down': |
| 410 | + switch ( unit ) { |
| 411 | + case 'unit': |
| 412 | + case 'char': |
| 413 | + /* |
| 414 | + * Looks for the in-document character position that would match up with the |
| 415 | + * same horizontal position - jumping a few pixels up/down at a time until we |
| 416 | + * reach the next/previous line |
| 417 | + */ |
| 418 | + var position = this.documentView.getRenderedPositionFromOffset( |
378 | 419 | this.selection.to |
379 | | - ); |
380 | | - newTo = instruction === 'home' ? range.start : range.end; |
381 | | - break; |
382 | | - case 'up' : |
383 | | - case 'down' : |
384 | | - /* |
385 | | - * Looks for the in-document character position that would match up with the same |
386 | | - * horizontal position - jumping a few pixels up/down at a time until we reach |
387 | | - * the next/previous line |
388 | | - */ |
389 | | - |
390 | | - var position = this.documentView.getRenderedPositionFromOffset( this.selection.to ); |
391 | | - if ( this.cursor.initialLeft === null ) { |
392 | | - this.cursor.initialLeft = position.left; |
| 420 | + ); |
| 421 | + if ( this.cursor.initialLeft === null ) { |
| 422 | + this.cursor.initialLeft = position.left; |
| 423 | + } |
| 424 | + var fakePosition = new es.Position( this.cursor.initialLeft, position.top ), |
| 425 | + i = 0, |
| 426 | + step = direction === 'up' ? -5 : 5, |
| 427 | + top = this.$.position().top; |
| 428 | + do { |
| 429 | + fakePosition.top += ++i * step; |
| 430 | + if ( fakePosition.top < top ) { |
| 431 | + break; |
| 432 | + } else if (fakePosition.top > top + this.dimensions.height + this.dimensions.scrollTop ) { |
| 433 | + break; |
| 434 | + } |
| 435 | + fakePosition = this.documentView.getRenderedPositionFromOffset( |
| 436 | + this.documentView.getOffsetFromRenderedPosition( fakePosition ) |
| 437 | + ); |
| 438 | + fakePosition.left = this.cursor.initialLeft; |
| 439 | + } while ( position.top === fakePosition.top ); |
| 440 | + to = this.documentView.getOffsetFromRenderedPosition( fakePosition ); |
| 441 | + break; |
393 | 442 | } |
394 | | - var fakePosition = new es.Position( this.cursor.initialLeft, position.top ), |
395 | | - i = 0, |
396 | | - step = instruction === 'up' ? -5 : 5, |
397 | | - top = this.$.position().top; |
398 | | - do { |
399 | | - fakePosition.top += ++i * step; |
400 | | - if ( fakePosition.top < top ) { |
401 | | - this.cursor.initialLeft = null; |
402 | | - fakePosition.top = fakePosition.left = 0; |
403 | | - break; |
404 | | - } else if ( fakePosition.top > top + this.dimensions.height + this.dimensions.scrollTop ) { |
405 | | - this.cursor.initialLeft = null; |
406 | | - fakePosition.left = this.dimensions.width; |
407 | | - break; |
408 | | - } |
409 | | - fakePosition = this.documentView.getRenderedPositionFromOffset( |
410 | | - this.documentView.getOffsetFromRenderedPosition( fakePosition ) |
411 | | - ); |
412 | | - fakePosition.left = this.cursor.initialLeft; |
413 | | - } while ( position.top === fakePosition.top ); |
414 | | - newTo = this.documentView.getOffsetFromRenderedPosition( fakePosition ); |
415 | | - break; |
| 443 | + break; |
416 | 444 | } |
417 | 445 | |
| 446 | + this.cursor.initialBias = direction === 'right' && unit === 'line' ? true : false; |
418 | 447 | |
419 | | - if( instruction === 'end' ) { |
420 | | - this.cursor.initialBias = true; |
| 448 | + if ( this.keyboard.keys.shift && this.selection.from !== to) { |
| 449 | + this.selection.to = to; |
| 450 | + this.documentView.drawSelection( this.selection ); |
| 451 | + this.hideCursor(); |
421 | 452 | } else { |
422 | | - this.cursor.initialBias = false; |
423 | | - } |
424 | | - |
425 | | - if ( this.keyboard.keys.shift ) { |
426 | | - this.selection.to = newTo; |
427 | | - if ( this.selection.from !== this.selection.to ) { |
428 | | - this.documentView.drawSelection( this.selection ); |
429 | | - this.hideCursor(); |
430 | | - } else { |
431 | | - this.documentView.clearSelection(); |
432 | | - this.showCursor(); |
433 | | - } |
434 | | - } else { |
435 | 453 | if ( this.selection.from !== this.selection.to ) { |
436 | 454 | this.documentView.clearSelection(); |
437 | 455 | } |
438 | | - this.selection.from = this.selection.to = newTo; |
| 456 | + this.selection.from = this.selection.to = to; |
439 | 457 | this.showCursor(); |
440 | 458 | } |
441 | | - this.emitSelect(); |
| 459 | + this.emitSelect(); |
442 | 460 | }; |
443 | 461 | |
444 | 462 | /** |