| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * ImageEditor is the top level object that holds together and connects | 6 * ImageEditor is the top level object that holds together and connects |
| 7 * everything needed for image editing. | 7 * everything needed for image editing. |
| 8 * | 8 * |
| 9 * @param {!Viewport} viewport The viewport. | 9 * @param {!Viewport} viewport The viewport. |
| 10 * @param {!ImageView} imageView The ImageView containing the images to edit. | 10 * @param {!ImageView} imageView The ImageView containing the images to edit. |
| 11 * @param {!ImageEditorPrompt} prompt Prompt instance. | 11 * @param {!ImageEditorPrompt} prompt Prompt instance. |
| 12 * @param {!Object} DOMContainers Various DOM containers required for the | 12 * @param {!{image: !HTMLElement, root: !HTMLElement, toolbar: !HTMLElement, |
| 13 * mode: !HTMLElement}} DOMContainers Various DOM containers required for the |
| 13 * editor. | 14 * editor. |
| 14 * @param {!Array<!ImageEditor.Mode>} modes Available editor modes. | 15 * @param {!Array<!ImageEditorMode>} modes Available editor modes. |
| 15 * @param {function(string, ...string)} displayStringFunction String | 16 * @param {function(string, ...string)} displayStringFunction String |
| 16 * formatting function. | 17 * formatting function. |
| 17 * @constructor | 18 * @constructor |
| 18 * @extends {cr.EventTarget} | 19 * @extends {cr.EventTarget} |
| 19 * @struct | 20 * @struct |
| 20 * | 21 * |
| 21 * TODO(yawano): Remove displayStringFunction from arguments. | 22 * TODO(yawano): Remove displayStringFunction from arguments. |
| 22 */ | 23 */ |
| 23 function ImageEditor( | 24 function ImageEditor( |
| 24 viewport, imageView, prompt, DOMContainers, modes, displayStringFunction) { | 25 viewport, imageView, prompt, DOMContainers, modes, displayStringFunction) { |
| 25 cr.EventTarget.call(this); | 26 cr.EventTarget.call(this); |
| 26 | 27 |
| 27 this.rootContainer_ = DOMContainers.root; | 28 this.rootContainer_ = DOMContainers.root; |
| 28 this.container_ = DOMContainers.image; | 29 this.container_ = DOMContainers.image; |
| 29 this.modes_ = modes; | 30 this.modes_ = modes; |
| 30 this.displayStringFunction_ = displayStringFunction; | 31 this.displayStringFunction_ = displayStringFunction; |
| 31 | 32 |
| 32 /** | 33 /** |
| 33 * @private {ImageEditor.Mode} | 34 * @private {ImageEditorMode} |
| 34 */ | 35 */ |
| 35 this.currentMode_ = null; | 36 this.currentMode_ = null; |
| 36 | 37 |
| 37 /** | 38 /** |
| 38 * @private {HTMLElement} | 39 * @private {HTMLElement} |
| 39 */ | 40 */ |
| 40 this.currentTool_ = null; | 41 this.currentTool_ = null; |
| 41 | 42 |
| 42 /** | 43 /** |
| 43 * @private {boolean} | 44 * @private {boolean} |
| 44 */ | 45 */ |
| 45 this.settingUpNextMode_ = false; | 46 this.settingUpNextMode_ = false; |
| 46 | 47 |
| 47 ImageUtil.removeChildren(this.container_); | 48 ImageUtil.removeChildren(this.container_); |
| 48 | 49 |
| 49 this.viewport_ = viewport; | 50 this.viewport_ = viewport; |
| 50 | 51 |
| 51 this.imageView_ = imageView; | 52 this.imageView_ = imageView; |
| 52 | 53 |
| 53 this.buffer_ = new ImageBuffer(); | 54 this.buffer_ = new ImageBuffer(); |
| 54 this.buffer_.addOverlay(this.imageView_); | 55 this.buffer_.addOverlay(this.imageView_); |
| 55 | 56 |
| 56 this.panControl_ = new ImageEditor.MouseControl( | 57 this.panControl_ = new ImageEditor.MouseControl( |
| 57 this.rootContainer_, this.container_, this.getBuffer()); | 58 this.rootContainer_, this.container_, this.getBuffer()); |
| 58 this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this)); | 59 this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this)); |
| 59 | 60 |
| 60 this.mainToolbar_ = new ImageEditor.Toolbar( | 61 this.mainToolbar_ = |
| 61 DOMContainers.toolbar, displayStringFunction); | 62 new ImageEditorToolbar(DOMContainers.toolbar, displayStringFunction); |
| 62 | 63 |
| 63 this.modeToolbar_ = new ImageEditor.Toolbar( | 64 this.modeToolbar_ = new ImageEditorToolbar( |
| 64 DOMContainers.mode, displayStringFunction, | 65 DOMContainers.mode, displayStringFunction, |
| 65 this.onOptionsChange.bind(this), true /* done button */); | 66 this.onOptionsChange.bind(this), true /* done button */); |
| 66 this.modeToolbar_.addEventListener( | 67 this.modeToolbar_.addEventListener( |
| 67 'done-clicked', this.onDoneClicked_.bind(this)); | 68 'done-clicked', this.onDoneClicked_.bind(this)); |
| 68 this.modeToolbar_.addEventListener( | 69 this.modeToolbar_.addEventListener( |
| 69 'cancel-clicked', this.onCancelClicked_.bind(this)); | 70 'cancel-clicked', this.onCancelClicked_.bind(this)); |
| 70 | 71 |
| 71 this.prompt_ = prompt; | 72 this.prompt_ = prompt; |
| 72 | 73 |
| 73 this.commandQueue_ = null; | 74 this.commandQueue_ = null; |
| 74 | 75 |
| 75 // ----------------------------------------------------------------- | 76 // ----------------------------------------------------------------- |
| 76 // Populate the toolbar. | 77 // Populate the toolbar. |
| 77 | 78 |
| 78 /** | 79 /** |
| 79 * @type {!Array<string>} | 80 * @type {!Array<string>} |
| 80 * @private | 81 * @private |
| 81 */ | 82 */ |
| 82 this.actionNames_ = []; | 83 this.actionNames_ = []; |
| 83 | 84 |
| 84 this.mainToolbar_.clear(); | 85 this.mainToolbar_.clear(); |
| 85 | 86 |
| 86 // Create action buttons. | 87 // Create action buttons. |
| 87 for (var i = 0; i != this.modes_.length; i++) { | 88 for (var i = 0; i != this.modes_.length; i++) { |
| 88 var mode = this.modes_[i]; | 89 var mode = this.modes_[i]; |
| 89 mode.bind(this, this.createToolButton_(mode.name, mode.title, | 90 var button = this.createToolButton_( |
| 90 this.enterMode.bind(this, mode), | 91 mode.name, mode.title, this.enterMode.bind(this, mode), mode.instant); |
| 91 mode.instant)); | 92 mode.bind( |
| 93 button, this.getBuffer(), this.getViewport(), this.getImageView()); |
| 94 this.registerAction_(mode.name); |
| 92 } | 95 } |
| 93 | 96 |
| 94 /** | 97 /** |
| 95 * @type {!HTMLElement} | 98 * @type {!HTMLElement} |
| 96 * @private | 99 * @private |
| 97 */ | 100 */ |
| 98 this.undoButton_ = this.createToolButton_('undo', 'GALLERY_UNDO', | 101 this.undoButton_ = this.createToolButton_('undo', 'GALLERY_UNDO', |
| 99 this.undo.bind(this), | 102 this.undo.bind(this), |
| 100 true /* instant */); | 103 true /* instant */); |
| 101 this.registerAction_('undo'); | 104 this.registerAction_('undo'); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 * @param {string} title Button title. | 144 * @param {string} title Button title. |
| 142 * @param {function(Event)} handler onClick handler. | 145 * @param {function(Event)} handler onClick handler. |
| 143 * @param {boolean} isInstant True if this tool (mode) is instant. | 146 * @param {boolean} isInstant True if this tool (mode) is instant. |
| 144 * @return {!HTMLElement} A created button. | 147 * @return {!HTMLElement} A created button. |
| 145 * @private | 148 * @private |
| 146 */ | 149 */ |
| 147 ImageEditor.prototype.createToolButton_ = function( | 150 ImageEditor.prototype.createToolButton_ = function( |
| 148 name, title, handler, isInstant) { | 151 name, title, handler, isInstant) { |
| 149 var button = this.mainToolbar_.addButton( | 152 var button = this.mainToolbar_.addButton( |
| 150 title, | 153 title, |
| 151 isInstant ? ImageEditor.Toolbar.ButtonType.ICON : | 154 isInstant ? ImageEditorToolbar.ButtonType.ICON : |
| 152 ImageEditor.Toolbar.ButtonType.ICON_TOGGLEABLE, | 155 ImageEditorToolbar.ButtonType.ICON_TOGGLEABLE, |
| 153 handler, | 156 handler, name /* opt_className */); |
| 154 name /* opt_className */); | |
| 155 return button; | 157 return button; |
| 156 }; | 158 }; |
| 157 | 159 |
| 158 /** | 160 /** |
| 159 * @return {boolean} True if no user commands are to be accepted. | 161 * @return {boolean} True if no user commands are to be accepted. |
| 160 */ | 162 */ |
| 161 ImageEditor.prototype.isLocked = function() { | 163 ImageEditor.prototype.isLocked = function() { |
| 162 return !this.commandQueue_ || this.commandQueue_.isBusy(); | 164 return !this.commandQueue_ || this.commandQueue_.isBusy(); |
| 163 }; | 165 }; |
| 164 | 166 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 | 219 |
| 218 var self = this; | 220 var self = this; |
| 219 this.imageView_.load( | 221 this.imageView_.load( |
| 220 item, effect, displayCallback, function(loadType, delay, error) { | 222 item, effect, displayCallback, function(loadType, delay, error) { |
| 221 self.lockUI(false); | 223 self.lockUI(false); |
| 222 | 224 |
| 223 // Always handle an item as original for new session. | 225 // Always handle an item as original for new session. |
| 224 item.setAsOriginal(); | 226 item.setAsOriginal(); |
| 225 | 227 |
| 226 self.commandQueue_ = new CommandQueue( | 228 self.commandQueue_ = new CommandQueue( |
| 227 self.container_.ownerDocument, assert(self.imageView_.getImage()), | 229 assert(self.container_.ownerDocument), |
| 228 saveFunction); | 230 assert(self.imageView_.getImage()), saveFunction); |
| 229 self.commandQueue_.attachUI( | 231 self.commandQueue_.attachUI( |
| 230 self.getImageView(), self.getPrompt(), self.filesToast_, | 232 self.getImageView(), self.getPrompt(), self.filesToast_, |
| 231 self.updateUndoRedo.bind(self), self.lockUI.bind(self)); | 233 self.updateUndoRedo.bind(self), self.lockUI.bind(self)); |
| 232 self.updateUndoRedo(); | 234 self.updateUndoRedo(); |
| 233 loadCallback(loadType, delay, error); | 235 loadCallback(loadType, delay, error); |
| 234 }); | 236 }); |
| 235 }; | 237 }; |
| 236 | 238 |
| 237 /** | 239 /** |
| 238 * Close the current image editing session. | 240 * Close the current image editing session. |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 | 288 |
| 287 /** | 289 /** |
| 288 * Undo the recently executed command. | 290 * Undo the recently executed command. |
| 289 */ | 291 */ |
| 290 ImageEditor.prototype.undo = function() { | 292 ImageEditor.prototype.undo = function() { |
| 291 if (this.isLocked()) return; | 293 if (this.isLocked()) return; |
| 292 this.recordToolUse('undo'); | 294 this.recordToolUse('undo'); |
| 293 | 295 |
| 294 // First undo click should dismiss the uncommitted modifications. | 296 // First undo click should dismiss the uncommitted modifications. |
| 295 if (this.currentMode_ && this.currentMode_.isUpdated()) { | 297 if (this.currentMode_ && this.currentMode_.isUpdated()) { |
| 298 this.modeToolbar_.reset(); |
| 296 this.currentMode_.reset(); | 299 this.currentMode_.reset(); |
| 297 return; | 300 return; |
| 298 } | 301 } |
| 299 | 302 |
| 300 this.getPrompt().hide(); | 303 this.getPrompt().hide(); |
| 301 this.leaveModeInternal_(false, false /* not to switch mode */); | 304 this.leaveModeInternal_(false, false /* not to switch mode */); |
| 302 this.commandQueue_.undo(); | 305 this.commandQueue_.undo(); |
| 303 this.updateUndoRedo(); | 306 this.updateUndoRedo(); |
| 304 this.calculateModeApplicativity_(); | 307 this.calculateModeApplicativity_(); |
| 305 }; | 308 }; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 360 */ | 363 */ |
| 361 ImageEditor.prototype.onOptionsChange = function(options) { | 364 ImageEditor.prototype.onOptionsChange = function(options) { |
| 362 ImageUtil.trace.resetTimer('update'); | 365 ImageUtil.trace.resetTimer('update'); |
| 363 if (this.currentMode_) { | 366 if (this.currentMode_) { |
| 364 this.currentMode_.update(options); | 367 this.currentMode_.update(options); |
| 365 } | 368 } |
| 366 ImageUtil.trace.reportTimer('update'); | 369 ImageUtil.trace.reportTimer('update'); |
| 367 }; | 370 }; |
| 368 | 371 |
| 369 /** | 372 /** |
| 370 * ImageEditor.Mode represents a modal state dedicated to a specific operation. | |
| 371 * Inherits from ImageBuffer. Overlay to simplify the drawing of mode-specific | |
| 372 * tools. | |
| 373 * | |
| 374 * @param {string} name The mode name. | |
| 375 * @param {string} title The mode title. | |
| 376 * @constructor | |
| 377 * @struct | |
| 378 * @extends {ImageBuffer.Overlay} | |
| 379 */ | |
| 380 ImageEditor.Mode = function(name, title) { | |
| 381 this.name = name; | |
| 382 this.title = title; | |
| 383 this.message_ = 'GALLERY_ENTER_WHEN_DONE'; | |
| 384 | |
| 385 /** | |
| 386 * @type {boolean} | |
| 387 */ | |
| 388 this.implicitCommit = false; | |
| 389 | |
| 390 /** | |
| 391 * @type {boolean} | |
| 392 */ | |
| 393 this.instant = false; | |
| 394 | |
| 395 /** | |
| 396 * @type {number} | |
| 397 */ | |
| 398 this.paddingTop = 0; | |
| 399 | |
| 400 /** | |
| 401 * @type {number} | |
| 402 */ | |
| 403 this.paddingBottom = 0; | |
| 404 | |
| 405 /** | |
| 406 * @type {ImageEditor} | |
| 407 * @private | |
| 408 */ | |
| 409 this.editor_ = null; | |
| 410 | |
| 411 /** | |
| 412 * @type {Viewport} | |
| 413 * @private | |
| 414 */ | |
| 415 this.viewport_ = null; | |
| 416 | |
| 417 /** | |
| 418 * @type {HTMLElement} | |
| 419 * @private | |
| 420 */ | |
| 421 this.button_ = null; | |
| 422 | |
| 423 /** | |
| 424 * @type {boolean} | |
| 425 * @private | |
| 426 */ | |
| 427 this.updated_ = false; | |
| 428 | |
| 429 /** | |
| 430 * @type {ImageView} | |
| 431 * @private | |
| 432 */ | |
| 433 this.imageView_ = null; | |
| 434 }; | |
| 435 | |
| 436 ImageEditor.Mode.prototype = { __proto__: ImageBuffer.Overlay.prototype }; | |
| 437 | |
| 438 /** | |
| 439 * @return {Viewport} Viewport instance. | |
| 440 */ | |
| 441 ImageEditor.Mode.prototype.getViewport = function() { return this.viewport_; }; | |
| 442 | |
| 443 /** | |
| 444 * @return {ImageView} ImageView instance. | |
| 445 */ | |
| 446 ImageEditor.Mode.prototype.getImageView = function() { | |
| 447 return this.imageView_; | |
| 448 }; | |
| 449 | |
| 450 /** | |
| 451 * @return {string} The mode-specific message to be displayed when entering. | |
| 452 */ | |
| 453 ImageEditor.Mode.prototype.getMessage = function() { return this.message_; }; | |
| 454 | |
| 455 /** | |
| 456 * @return {boolean} True if the mode is applicable in the current context. | |
| 457 */ | |
| 458 ImageEditor.Mode.prototype.isApplicable = function() { return true; }; | |
| 459 | |
| 460 /** | |
| 461 * Called once after creating the mode button. | |
| 462 * | |
| 463 * @param {!ImageEditor} editor The editor instance. | |
| 464 * @param {!HTMLElement} button The mode button. | |
| 465 */ | |
| 466 | |
| 467 ImageEditor.Mode.prototype.bind = function(editor, button) { | |
| 468 this.editor_ = editor; | |
| 469 this.editor_.registerAction_(this.name); | |
| 470 this.button_ = button; | |
| 471 this.viewport_ = editor.getViewport(); | |
| 472 this.imageView_ = editor.getImageView(); | |
| 473 }; | |
| 474 | |
| 475 /** | |
| 476 * Called before entering the mode. | |
| 477 */ | |
| 478 ImageEditor.Mode.prototype.setUp = function() { | |
| 479 this.editor_.getBuffer().addOverlay(this); | |
| 480 this.updated_ = false; | |
| 481 }; | |
| 482 | |
| 483 /** | |
| 484 * Create mode-specific controls here. | |
| 485 * @param {!ImageEditor.Toolbar} toolbar The toolbar to populate. | |
| 486 */ | |
| 487 ImageEditor.Mode.prototype.createTools = function(toolbar) {}; | |
| 488 | |
| 489 /** | |
| 490 * Called before exiting the mode. | |
| 491 */ | |
| 492 ImageEditor.Mode.prototype.cleanUpUI = function() { | |
| 493 this.editor_.getBuffer().removeOverlay(this); | |
| 494 }; | |
| 495 | |
| 496 /** | |
| 497 * Called after exiting the mode. | |
| 498 */ | |
| 499 ImageEditor.Mode.prototype.cleanUpCaches = function() {}; | |
| 500 | |
| 501 /** | |
| 502 * Called when any of the controls changed its value. | |
| 503 * @param {Object} options A map of options. | |
| 504 */ | |
| 505 ImageEditor.Mode.prototype.update = function(options) { | |
| 506 this.markUpdated(); | |
| 507 }; | |
| 508 | |
| 509 /** | |
| 510 * Mark the editor mode as updated. | |
| 511 */ | |
| 512 ImageEditor.Mode.prototype.markUpdated = function() { | |
| 513 this.updated_ = true; | |
| 514 }; | |
| 515 | |
| 516 /** | |
| 517 * @return {boolean} True if the mode controls changed. | |
| 518 */ | |
| 519 ImageEditor.Mode.prototype.isUpdated = function() { return this.updated_; }; | |
| 520 | |
| 521 /** | |
| 522 * @return {boolean} True if a key event should be consumed by the mode. | |
| 523 */ | |
| 524 ImageEditor.Mode.prototype.isConsumingKeyEvents = function() { return false; }; | |
| 525 | |
| 526 /** | |
| 527 * Resets the mode to a clean state. | |
| 528 */ | |
| 529 ImageEditor.Mode.prototype.reset = function() { | |
| 530 this.editor_.modeToolbar_.reset(); | |
| 531 this.updated_ = false; | |
| 532 }; | |
| 533 | |
| 534 /** | |
| 535 * @return {Command} Command. | |
| 536 */ | |
| 537 ImageEditor.Mode.prototype.getCommand = function() { | |
| 538 return null; | |
| 539 }; | |
| 540 | |
| 541 /** | |
| 542 * One-click editor tool, requires no interaction, just executes the command. | |
| 543 * | |
| 544 * @param {string} name The mode name. | |
| 545 * @param {string} title The mode title. | |
| 546 * @param {!Command} command The command to execute on click. | |
| 547 * @constructor | |
| 548 * @extends {ImageEditor.Mode} | |
| 549 * @struct | |
| 550 */ | |
| 551 ImageEditor.Mode.OneClick = function(name, title, command) { | |
| 552 ImageEditor.Mode.call(this, name, title); | |
| 553 this.instant = true; | |
| 554 this.command_ = command; | |
| 555 }; | |
| 556 | |
| 557 ImageEditor.Mode.OneClick.prototype = {__proto__: ImageEditor.Mode.prototype}; | |
| 558 | |
| 559 /** | |
| 560 * @override | |
| 561 */ | |
| 562 ImageEditor.Mode.OneClick.prototype.getCommand = function() { | |
| 563 return this.command_; | |
| 564 }; | |
| 565 | |
| 566 /** | |
| 567 * Register the action name. Required for metrics reporting. | 373 * Register the action name. Required for metrics reporting. |
| 568 * @param {string} name Button name. | 374 * @param {string} name Button name. |
| 569 * @private | 375 * @private |
| 570 */ | 376 */ |
| 571 ImageEditor.prototype.registerAction_ = function(name) { | 377 ImageEditor.prototype.registerAction_ = function(name) { |
| 572 this.actionNames_.push(name); | 378 this.actionNames_.push(name); |
| 573 }; | 379 }; |
| 574 | 380 |
| 575 /** | 381 /** |
| 576 * @return {ImageEditor.Mode} The current mode. | 382 * @return {ImageEditorMode} The current mode. |
| 577 */ | 383 */ |
| 578 ImageEditor.prototype.getMode = function() { return this.currentMode_; }; | 384 ImageEditor.prototype.getMode = function() { return this.currentMode_; }; |
| 579 | 385 |
| 580 /** | 386 /** |
| 581 * The user clicked on the mode button. | 387 * The user clicked on the mode button. |
| 582 * | 388 * |
| 583 * @param {!ImageEditor.Mode} mode The new mode. | 389 * @param {!ImageEditorMode} mode The new mode. |
| 584 */ | 390 */ |
| 585 ImageEditor.prototype.enterMode = function(mode) { | 391 ImageEditor.prototype.enterMode = function(mode) { |
| 586 if (this.isLocked()) return; | 392 if (this.isLocked()) return; |
| 587 | 393 |
| 588 if (this.currentMode_ === mode) { | 394 if (this.currentMode_ === mode) { |
| 589 // Currently active editor tool clicked, commit if modified. | 395 // Currently active editor tool clicked, commit if modified. |
| 590 this.leaveModeInternal_( | 396 this.leaveModeInternal_( |
| 591 this.currentMode_.updated_, false /* not to switch mode */); | 397 this.currentMode_.updated_, false /* not to switch mode */); |
| 592 return; | 398 return; |
| 593 } | 399 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 606 // with the mode set up. | 412 // with the mode set up. |
| 607 this.commandQueue_.executeWhenReady(function() { | 413 this.commandQueue_.executeWhenReady(function() { |
| 608 this.setUpMode_(mode); | 414 this.setUpMode_(mode); |
| 609 this.settingUpNextMode_ = false; | 415 this.settingUpNextMode_ = false; |
| 610 }.bind(this)); | 416 }.bind(this)); |
| 611 }; | 417 }; |
| 612 | 418 |
| 613 /** | 419 /** |
| 614 * Set up the new editing mode. | 420 * Set up the new editing mode. |
| 615 * | 421 * |
| 616 * @param {!ImageEditor.Mode} mode The mode. | 422 * @param {!ImageEditorMode} mode The mode. |
| 617 * @private | 423 * @private |
| 618 */ | 424 */ |
| 619 ImageEditor.prototype.setUpMode_ = function(mode) { | 425 ImageEditor.prototype.setUpMode_ = function(mode) { |
| 620 this.currentTool_ = mode.button_; | 426 this.currentTool_ = mode.button_; |
| 621 this.currentMode_ = mode; | 427 this.currentMode_ = mode; |
| 622 this.rootContainer_.setAttribute('editor-mode', mode.name); | 428 this.rootContainer_.setAttribute('editor-mode', mode.name); |
| 623 | 429 |
| 624 // Activate toggle ripple if button is toggleable. | 430 // Activate toggle ripple if button is toggleable. |
| 625 var filesToggleRipple = | 431 var filesToggleRipple = |
| 626 this.currentTool_.querySelector('files-toggle-ripple'); | 432 this.currentTool_.querySelector('files-toggle-ripple'); |
| 627 if (filesToggleRipple) { | 433 if (filesToggleRipple) { |
| 628 // Current mode must NOT be instant for toggleable button. | 434 // Current mode must NOT be instant for toggleable button. |
| 629 assert(!this.currentMode_.instant); | 435 assert(!this.currentMode_.instant); |
| 630 filesToggleRipple.activated = true; | 436 filesToggleRipple.activated = true; |
| 631 } | 437 } |
| 632 | 438 |
| 633 // Scale the screen so that it doesn't overlap the toolbars. We should scale | 439 // Scale the screen so that it doesn't overlap the toolbars. We should scale |
| 634 // the screen before setup of current mode is called to make the current mode | 440 // the screen before setup of current mode is called to make the current mode |
| 635 // able to set up with new screen size. | 441 // able to set up with new screen size. |
| 636 if (!this.currentMode_.instant) { | 442 if (!this.currentMode_.instant) { |
| 637 this.getViewport().setScreenTop( | 443 this.getViewport().setScreenTop( |
| 638 ImageEditor.Toolbar.HEIGHT + mode.paddingTop); | 444 ImageEditorToolbar.HEIGHT + mode.paddingTop); |
| 639 this.getViewport().setScreenBottom( | 445 this.getViewport().setScreenBottom( |
| 640 ImageEditor.Toolbar.HEIGHT * 2 + mode.paddingBottom); | 446 ImageEditorToolbar.HEIGHT * 2 + mode.paddingBottom); |
| 641 this.getImageView().applyViewportChange(); | 447 this.getImageView().applyViewportChange(); |
| 642 } | 448 } |
| 643 | 449 |
| 644 this.currentMode_.setUp(); | 450 this.currentMode_.setUp(); |
| 645 | 451 |
| 646 this.calculateModeApplicativity_(); | 452 this.calculateModeApplicativity_(); |
| 647 if (this.currentMode_.instant) { // Instant tool. | 453 if (this.currentMode_.instant) { // Instant tool. |
| 648 this.leaveModeInternal_(true, false /* not to switch mode */); | 454 this.leaveModeInternal_(true, false /* not to switch mode */); |
| 649 return; | 455 return; |
| 650 } | 456 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 681 * @param {boolean} leaveToSwitchMode True if it leaves to change mode. | 487 * @param {boolean} leaveToSwitchMode True if it leaves to change mode. |
| 682 * @private | 488 * @private |
| 683 */ | 489 */ |
| 684 ImageEditor.prototype.leaveModeInternal_ = function(commit, leaveToSwitchMode) { | 490 ImageEditor.prototype.leaveModeInternal_ = function(commit, leaveToSwitchMode) { |
| 685 if (!this.currentMode_) | 491 if (!this.currentMode_) |
| 686 return; | 492 return; |
| 687 | 493 |
| 688 // If the current mode is 'Resize', and commit is required, | 494 // If the current mode is 'Resize', and commit is required, |
| 689 // leaving mode should be stopped when an input value is not valid. | 495 // leaving mode should be stopped when an input value is not valid. |
| 690 if(commit && this.currentMode_.name === 'resize') { | 496 if(commit && this.currentMode_.name === 'resize') { |
| 691 var resizeMode = /** @type {!ImageEditor.Mode.Resize} */ | 497 var resizeMode = /** @type {!ImageEditorMode.Resize} */ |
| 692 (this.currentMode_); | 498 (this.currentMode_); |
| 693 if(!resizeMode.isInputValid()) { | 499 if(!resizeMode.isInputValid()) { |
| 694 resizeMode.showAlertDialog(); | 500 resizeMode.showAlertDialog(); |
| 695 return; | 501 return; |
| 696 } | 502 } |
| 697 } | 503 } |
| 698 | 504 |
| 699 this.modeToolbar_.show(false); | 505 this.modeToolbar_.show(false); |
| 700 this.rootContainer_.removeAttribute('editor-mode'); | 506 this.rootContainer_.removeAttribute('editor-mode'); |
| 701 | 507 |
| 702 // If it leaves to switch mode, do not restore screen size since the next mode | 508 // If it leaves to switch mode, do not restore screen size since the next mode |
| 703 // might change screen size. We should avoid to show intermediate animation | 509 // might change screen size. We should avoid to show intermediate animation |
| 704 // which tries to restore screen size. | 510 // which tries to restore screen size. |
| 705 if (!leaveToSwitchMode) { | 511 if (!leaveToSwitchMode) { |
| 706 this.getViewport().setScreenTop(ImageEditor.Toolbar.HEIGHT); | 512 this.getViewport().setScreenTop(ImageEditorToolbar.HEIGHT); |
| 707 this.getViewport().setScreenBottom(ImageEditor.Toolbar.HEIGHT); | 513 this.getViewport().setScreenBottom(ImageEditorToolbar.HEIGHT); |
| 708 this.getImageView().applyViewportChange(); | 514 this.getImageView().applyViewportChange(); |
| 709 } | 515 } |
| 710 | 516 |
| 711 this.currentMode_.cleanUpUI(); | 517 this.currentMode_.cleanUpUI(); |
| 712 | 518 |
| 713 if (commit) { | 519 if (commit) { |
| 714 var self = this; | 520 var self = this; |
| 715 var command = this.currentMode_.getCommand(); | 521 var command = this.currentMode_.getCommand(); |
| 716 if (command) { // Could be null if the user did not do anything. | 522 if (command) { // Could be null if the user did not do anything. |
| 717 this.commandQueue_.execute(command); | 523 this.commandQueue_.execute(command); |
| (...skipping 394 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1112 * @param {!Object} position An object holding x and y properties. | 918 * @param {!Object} position An object holding x and y properties. |
| 1113 * @private | 919 * @private |
| 1114 */ | 920 */ |
| 1115 ImageEditor.MouseControl.prototype.updateCursor_ = function(position) { | 921 ImageEditor.MouseControl.prototype.updateCursor_ = function(position) { |
| 1116 var oldCursor = this.container_.getAttribute('cursor'); | 922 var oldCursor = this.container_.getAttribute('cursor'); |
| 1117 var newCursor = this.buffer_.getCursorStyle( | 923 var newCursor = this.buffer_.getCursorStyle( |
| 1118 position.x, position.y, !!this.dragHandler_); | 924 position.x, position.y, !!this.dragHandler_); |
| 1119 if (newCursor != oldCursor) // Avoid flicker. | 925 if (newCursor != oldCursor) // Avoid flicker. |
| 1120 this.container_.setAttribute('cursor', newCursor); | 926 this.container_.setAttribute('cursor', newCursor); |
| 1121 }; | 927 }; |
| 1122 | |
| 1123 /** | |
| 1124 * A toolbar for the ImageEditor. | |
| 1125 * @param {!HTMLElement} parent The parent element. | |
| 1126 * @param {function(string)} displayStringFunction A string formatting function. | |
| 1127 * @param {function(Object)=} opt_updateCallback The callback called when | |
| 1128 * controls change. | |
| 1129 * @param {boolean=} opt_showActionButtons True to show action buttons. | |
| 1130 * @constructor | |
| 1131 * @extends {cr.EventTarget} | |
| 1132 * @struct | |
| 1133 */ | |
| 1134 ImageEditor.Toolbar = function( | |
| 1135 parent, displayStringFunction, opt_updateCallback, opt_showActionButtons) { | |
| 1136 this.wrapper_ = parent; | |
| 1137 this.displayStringFunction_ = displayStringFunction; | |
| 1138 | |
| 1139 /** | |
| 1140 * @type {?function(Object)} | |
| 1141 * @private | |
| 1142 */ | |
| 1143 this.updateCallback_ = opt_updateCallback || null; | |
| 1144 | |
| 1145 /** | |
| 1146 * @private {!HTMLElement} | |
| 1147 */ | |
| 1148 this.container_ = /** @type {!HTMLElement} */ (document.createElement('div')); | |
| 1149 this.container_.classList.add('container'); | |
| 1150 this.wrapper_.appendChild(this.container_); | |
| 1151 | |
| 1152 // Create action buttons. | |
| 1153 if (opt_showActionButtons) { | |
| 1154 var actionButtonsLayer = document.createElement('div'); | |
| 1155 actionButtonsLayer.classList.add('action-buttons'); | |
| 1156 | |
| 1157 this.cancelButton_ = ImageEditor.Toolbar.createButton_( | |
| 1158 'GALLERY_CANCEL_LABEL', ImageEditor.Toolbar.ButtonType.LABEL_UPPER_CASE, | |
| 1159 this.onCancelClicked_.bind(this), 'cancel'); | |
| 1160 actionButtonsLayer.appendChild(this.cancelButton_); | |
| 1161 | |
| 1162 this.doneButton_ = ImageEditor.Toolbar.createButton_( | |
| 1163 'GALLERY_DONE', ImageEditor.Toolbar.ButtonType.LABEL_UPPER_CASE, | |
| 1164 this.onDoneClicked_.bind(this), 'done'); | |
| 1165 actionButtonsLayer.appendChild(this.doneButton_); | |
| 1166 | |
| 1167 this.wrapper_.appendChild(actionButtonsLayer); | |
| 1168 } | |
| 1169 }; | |
| 1170 | |
| 1171 ImageEditor.Toolbar.prototype.__proto__ = cr.EventTarget.prototype; | |
| 1172 | |
| 1173 /** | |
| 1174 * Height of the toolbar. | |
| 1175 * @const {number} | |
| 1176 */ | |
| 1177 ImageEditor.Toolbar.HEIGHT = 48; // px | |
| 1178 | |
| 1179 /** | |
| 1180 * Handles click event of done button. | |
| 1181 * @private | |
| 1182 */ | |
| 1183 ImageEditor.Toolbar.prototype.onDoneClicked_ = function() { | |
| 1184 this.doneButton_.querySelector('paper-ripple').simulatedRipple(); | |
| 1185 | |
| 1186 var event = new Event('done-clicked'); | |
| 1187 this.dispatchEvent(event); | |
| 1188 }; | |
| 1189 | |
| 1190 /** | |
| 1191 * Handles click event of cancel button. | |
| 1192 * @private | |
| 1193 */ | |
| 1194 ImageEditor.Toolbar.prototype.onCancelClicked_ = function() { | |
| 1195 this.cancelButton_.querySelector('paper-ripple').simulatedRipple(); | |
| 1196 | |
| 1197 var event = new Event('cancel-clicked'); | |
| 1198 this.dispatchEvent(event); | |
| 1199 }; | |
| 1200 | |
| 1201 /** | |
| 1202 * Returns the parent element. | |
| 1203 * @return {!HTMLElement} | |
| 1204 */ | |
| 1205 ImageEditor.Toolbar.prototype.getElement = function() { | |
| 1206 return this.container_; | |
| 1207 }; | |
| 1208 | |
| 1209 /** | |
| 1210 * Clear the toolbar. | |
| 1211 */ | |
| 1212 ImageEditor.Toolbar.prototype.clear = function() { | |
| 1213 ImageUtil.removeChildren(this.container_); | |
| 1214 }; | |
| 1215 | |
| 1216 /** | |
| 1217 * Add a control. | |
| 1218 * @param {!HTMLElement} element The control to add. | |
| 1219 * @return {!HTMLElement} The added element. | |
| 1220 */ | |
| 1221 ImageEditor.Toolbar.prototype.add = function(element) { | |
| 1222 this.container_.appendChild(element); | |
| 1223 return element; | |
| 1224 }; | |
| 1225 | |
| 1226 /** | |
| 1227 * Button type. | |
| 1228 * @enum {string} | |
| 1229 */ | |
| 1230 ImageEditor.Toolbar.ButtonType = { | |
| 1231 ICON: 'icon', | |
| 1232 ICON_TOGGLEABLE: 'icon_toggleable', | |
| 1233 LABEL: 'label', | |
| 1234 LABEL_UPPER_CASE: 'label_upper_case' | |
| 1235 }; | |
| 1236 | |
| 1237 /** | |
| 1238 * Create a button. | |
| 1239 * | |
| 1240 * @param {string} title String ID of button title. | |
| 1241 * @param {ImageEditor.Toolbar.ButtonType} type Button type. | |
| 1242 * @param {function(Event)} handler onClick handler. | |
| 1243 * @param {string=} opt_class Extra class name. | |
| 1244 * @return {!HTMLElement} The created button. | |
| 1245 * @private | |
| 1246 */ | |
| 1247 ImageEditor.Toolbar.createButton_ = function( | |
| 1248 title, type, handler, opt_class) { | |
| 1249 var button = /** @type {!HTMLElement} */ (document.createElement('button')); | |
| 1250 if (opt_class) | |
| 1251 button.classList.add(opt_class); | |
| 1252 button.classList.add('edit-toolbar'); | |
| 1253 | |
| 1254 if (type === ImageEditor.Toolbar.ButtonType.ICON || | |
| 1255 type === ImageEditor.Toolbar.ButtonType.ICON_TOGGLEABLE) { | |
| 1256 var icon = document.createElement('div'); | |
| 1257 icon.classList.add('icon'); | |
| 1258 | |
| 1259 // Show tooltip for icon button. | |
| 1260 assertInstanceof(document.querySelector('files-tooltip'), FilesTooltip) | |
| 1261 .addTarget(button); | |
| 1262 | |
| 1263 button.appendChild(icon); | |
| 1264 | |
| 1265 if (type === ImageEditor.Toolbar.ButtonType.ICON) { | |
| 1266 var filesRipple = document.createElement('files-ripple'); | |
| 1267 button.appendChild(filesRipple); | |
| 1268 } else { | |
| 1269 var filesToggleRipple = document.createElement('files-toggle-ripple'); | |
| 1270 button.appendChild(filesToggleRipple); | |
| 1271 } | |
| 1272 } else if (type === ImageEditor.Toolbar.ButtonType.LABEL || | |
| 1273 type === ImageEditor.Toolbar.ButtonType.LABEL_UPPER_CASE) { | |
| 1274 var label = document.createElement('span'); | |
| 1275 label.classList.add('label'); | |
| 1276 label.textContent = | |
| 1277 type === ImageEditor.Toolbar.ButtonType.LABEL_UPPER_CASE ? | |
| 1278 strf(title).toLocaleUpperCase() : strf(title); | |
| 1279 | |
| 1280 button.appendChild(label); | |
| 1281 | |
| 1282 var paperRipple = document.createElement('paper-ripple'); | |
| 1283 button.appendChild(paperRipple); | |
| 1284 } else { | |
| 1285 assertNotReached(); | |
| 1286 } | |
| 1287 | |
| 1288 button.label = strf(title); | |
| 1289 button.setAttribute('aria-label', strf(title)); | |
| 1290 | |
| 1291 GalleryUtil.decorateMouseFocusHandling(button); | |
| 1292 | |
| 1293 button.addEventListener('click', handler, false); | |
| 1294 button.addEventListener('keydown', function(event) { | |
| 1295 // Stop propagation of Enter key event to prevent it from being captured by | |
| 1296 // image editor. | |
| 1297 if (event.key === 'Enter') | |
| 1298 event.stopPropagation(); | |
| 1299 }); | |
| 1300 | |
| 1301 return button; | |
| 1302 }; | |
| 1303 | |
| 1304 /** | |
| 1305 * Add a button. | |
| 1306 * | |
| 1307 * @param {string} title Button title. | |
| 1308 * @param {ImageEditor.Toolbar.ButtonType} type Button type. | |
| 1309 * @param {function(Event)} handler onClick handler. | |
| 1310 * @param {string=} opt_class Extra class name. | |
| 1311 * @return {!HTMLElement} The added button. | |
| 1312 */ | |
| 1313 ImageEditor.Toolbar.prototype.addButton = function( | |
| 1314 title, type, handler, opt_class) { | |
| 1315 var button = ImageEditor.Toolbar.createButton_( | |
| 1316 title, type, handler, opt_class); | |
| 1317 this.add(button); | |
| 1318 return button; | |
| 1319 }; | |
| 1320 | |
| 1321 /** | |
| 1322 * Add a input field. | |
| 1323 * | |
| 1324 * @param {string} name Input name | |
| 1325 * @param {string} title Input title | |
| 1326 * @param {function(Event)} handler onInput and onChange handler | |
| 1327 * @param {string|number} value Default value | |
| 1328 * @param {string=} opt_unit Unit for an input field | |
| 1329 * @return {!HTMLElement} Input Element | |
| 1330 */ | |
| 1331 ImageEditor.Toolbar.prototype.addInput = function( | |
| 1332 name, title, handler, value, opt_unit) { | |
| 1333 | |
| 1334 var input = /** @type {!HTMLElement} */ (document.createElement('div')); | |
| 1335 input.classList.add('input', name); | |
| 1336 | |
| 1337 var text = document.createElement('paper-input'); | |
| 1338 text.setAttribute('label', strf(title)); | |
| 1339 text.classList.add('text', name); | |
| 1340 text.value = value; | |
| 1341 | |
| 1342 // We should listen to not only 'change' event, but also 'input' because we | |
| 1343 // want to update values as soon as the user types characters. | |
| 1344 text.addEventListener('input', handler, false); | |
| 1345 text.addEventListener('change', handler, false); | |
| 1346 input.appendChild(text); | |
| 1347 | |
| 1348 if(opt_unit) { | |
| 1349 var unit_label = document.createElement('span'); | |
| 1350 unit_label.textContent = opt_unit; | |
| 1351 unit_label.classList.add('unit_label'); | |
| 1352 input.appendChild(unit_label); | |
| 1353 } | |
| 1354 | |
| 1355 input.name = name; | |
| 1356 input.getValue = function(text) { | |
| 1357 return text.value; | |
| 1358 }.bind(this, text); | |
| 1359 input.setValue = function(text, value) { | |
| 1360 text.value = value; | |
| 1361 }.bind(this, text); | |
| 1362 | |
| 1363 this.add(input); | |
| 1364 | |
| 1365 return input; | |
| 1366 }; | |
| 1367 | |
| 1368 /** | |
| 1369 * Add a range control (scalar value picker). | |
| 1370 * | |
| 1371 * @param {string} name An option name. | |
| 1372 * @param {string} title An option title. | |
| 1373 * @param {number} min Min value of the option. | |
| 1374 * @param {number} value Default value of the option. | |
| 1375 * @param {number} max Max value of the options. | |
| 1376 * @param {number=} opt_scale A number to multiply by when setting | |
| 1377 * min/value/max in DOM. | |
| 1378 * @param {boolean=} opt_showNumeric True if numeric value should be displayed. | |
| 1379 * @return {!HTMLElement} Range element. | |
| 1380 */ | |
| 1381 ImageEditor.Toolbar.prototype.addRange = function( | |
| 1382 name, title, min, value, max, opt_scale, opt_showNumeric) { | |
| 1383 var range = /** @type {!HTMLElement} */ (document.createElement('div')); | |
| 1384 range.classList.add('range', name); | |
| 1385 | |
| 1386 var icon = document.createElement('icon'); | |
| 1387 icon.classList.add('icon'); | |
| 1388 range.appendChild(icon); | |
| 1389 | |
| 1390 var label = document.createElement('span'); | |
| 1391 label.textContent = strf(title); | |
| 1392 label.classList.add('label'); | |
| 1393 range.appendChild(label); | |
| 1394 | |
| 1395 var scale = opt_scale || 1; | |
| 1396 var slider = document.createElement('paper-slider'); | |
| 1397 slider.min = Math.ceil(min * scale); | |
| 1398 slider.max = Math.floor(max * scale); | |
| 1399 slider.value = value * scale; | |
| 1400 slider.addEventListener('change', function(event) { | |
| 1401 if (this.updateCallback_) | |
| 1402 this.updateCallback_(this.getOptions()); | |
| 1403 }.bind(this)); | |
| 1404 range.appendChild(slider); | |
| 1405 | |
| 1406 range.name = name; | |
| 1407 range.getValue = function(slider, scale) { | |
| 1408 return slider.value / scale; | |
| 1409 }.bind(this, slider, scale); | |
| 1410 | |
| 1411 // Swallow the left and right keys, so they are not handled by other | |
| 1412 // listeners. | |
| 1413 range.addEventListener('keydown', function(e) { | |
| 1414 if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') | |
| 1415 e.stopPropagation(); | |
| 1416 }); | |
| 1417 | |
| 1418 this.add(range); | |
| 1419 | |
| 1420 return range; | |
| 1421 }; | |
| 1422 | |
| 1423 /** | |
| 1424 * @return {!Object} options A map of options. | |
| 1425 */ | |
| 1426 ImageEditor.Toolbar.prototype.getOptions = function() { | |
| 1427 var values = {}; | |
| 1428 | |
| 1429 for (var child = this.container_.firstChild; | |
| 1430 child; | |
| 1431 child = child.nextSibling) { | |
| 1432 if (child.name) | |
| 1433 values[child.name] = child.getValue(); | |
| 1434 } | |
| 1435 | |
| 1436 return values; | |
| 1437 }; | |
| 1438 | |
| 1439 /** | |
| 1440 * Reset the toolbar. | |
| 1441 */ | |
| 1442 ImageEditor.Toolbar.prototype.reset = function() { | |
| 1443 for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) { | |
| 1444 if (child.reset) child.reset(); | |
| 1445 } | |
| 1446 }; | |
| 1447 | |
| 1448 /** | |
| 1449 * Show/hide the toolbar. | |
| 1450 * @param {boolean} on True if show. | |
| 1451 */ | |
| 1452 ImageEditor.Toolbar.prototype.show = function(on) { | |
| 1453 if (!this.wrapper_.firstChild) | |
| 1454 return; // Do not show empty toolbar; | |
| 1455 | |
| 1456 this.wrapper_.hidden = !on; | |
| 1457 | |
| 1458 // Focus the first input on the toolbar. | |
| 1459 if (on) { | |
| 1460 var input = this.container_.querySelector( | |
| 1461 'button, paper-button, input, paper-input, paper-slider'); | |
| 1462 if (input) | |
| 1463 input.focus(); | |
| 1464 } | |
| 1465 }; | |
| OLD | NEW |