From: Aleksander Ɓukasz Date: Wed, 28 May 2014 08:22:09 +0000 (+0200) Subject: editor: introducing canvas gutter X-Git-Url: https://git.mdrn.pl/fnpeditor.git/commitdiff_plain/f1709cec5b085835c7e5d36eaa2d2b35d0b13f22?ds=sidebyside;hp=fa35a2ccd01eccad357d79258de134a4e0f9d6ea editor: introducing canvas gutter --- diff --git a/src/editor/modules/documentCanvas/canvas/canvas.js b/src/editor/modules/documentCanvas/canvas/canvas.js index f7edfa5..3bf6b51 100644 --- a/src/editor/modules/documentCanvas/canvas/canvas.js +++ b/src/editor/modules/documentCanvas/canvas/canvas.js @@ -10,7 +10,8 @@ define([ 'modules/documentCanvas/canvas/elementsRegister', 'modules/documentCanvas/canvas/genericElement', 'modules/documentCanvas/canvas/nullElement', -], function($, _, Backbone, logging, documentElement, keyboard, utils, wlxmlListener, ElementsRegister, genericElement, nullElement) { +'modules/documentCanvas/canvas/gutter', +], function($, _, Backbone, logging, documentElement, keyboard, utils, wlxmlListener, ElementsRegister, genericElement, nullElement, gutter) { 'use strict'; /* global document:false, window:false, Node:false, gettext */ @@ -73,7 +74,15 @@ var Canvas = function(wlxmlDocument, elements) { this.elementsRegister.register(elementDesc); }.bind(this)); this.eventBus = _.extend({}, Backbone.Events); - this.wrapper = $('
').addClass('canvas-wrapper').attr('contenteditable', true); + + this.wrapper = $('
'); + + + this.gutter = gutter.create(); + this.gutterView = new gutter.GutterView(this.gutter); + this.rootWrapper = $('
').addClass('canvas-wrapper').attr('contenteditable', true); + this.wrapper.find('.crow').append(this.rootWrapper, this.gutterView.dom); + this.wlxmlListener = wlxmlListener.create(this); this.loadWlxmlDocument(wlxmlDocument); this.setupEventHandling(); @@ -82,6 +91,10 @@ var Canvas = function(wlxmlDocument, elements) { $.extend(Canvas.prototype, Backbone.Events, { + getElementOffset: function(element) { + return element.dom.offset().top - this.wrapper.offset().top; + }, + loadWlxmlDocument: function(wlxmlDocument) { if(!wlxmlDocument) { return false; @@ -124,27 +137,27 @@ $.extend(Canvas.prototype, Backbone.Events, { reloadRoot: function() { this.rootElement = this.createElement(this.wlxmlDocument.root); - this.wrapper.empty(); - this.wrapper.append(this.rootElement.dom); + this.rootWrapper.empty(); + this.rootWrapper.append(this.rootElement.dom); }, setupEventHandling: function() { var canvas = this; - this.wrapper.on('keyup keydown keypress', function(e) { + this.rootWrapper.on('keyup keydown keypress', function(e) { keyboard.handleKey(e, canvas); }); - this.wrapper.on('mouseup', function() { + this.rootWrapper.on('mouseup', function() { canvas.triggerSelectionChanged(); }); var mouseDown; - this.wrapper.on('mousedown', '[document-node-element], [document-text-element]', function(e) { + this.rootWrapper.on('mousedown', '[document-node-element], [document-text-element]', function(e) { mouseDown = e.target; }); - this.wrapper.on('click', '[document-node-element], [document-text-element]', function(e) { + this.rootWrapper.on('click', '[document-node-element], [document-text-element]', function(e) { e.stopPropagation(); if(e.originalEvent.detail === 3) { e.preventDefault(); @@ -156,7 +169,7 @@ $.extend(Canvas.prototype, Backbone.Events, { } }); - this.wrapper.on('paste', function(e) { + this.rootWrapper.on('paste', function(e) { e.preventDefault(); var clipboardData = e.originalEvent.clipboardData; @@ -206,7 +219,7 @@ $.extend(Canvas.prototype, Backbone.Events, { }); }); var config = { attributes: false, childList: false, characterData: true, subtree: true, characterDataOldValue: true}; - observer.observe(this.wrapper[0], config); + observer.observe(this.rootWrapper[0], config); var hoverHandler = function(e) { @@ -225,8 +238,8 @@ $.extend(Canvas.prototype, Backbone.Events, { el.updateState({exposed:expose[e.type]}); }; - this.wrapper.on('mouseover', '[document-node-element], [document-text-element]', hoverHandler); - this.wrapper.on('mouseout', '[document-node-element], [document-text-element]', hoverHandler); + this.rootWrapper.on('mouseover', '[document-node-element], [document-text-element]', hoverHandler); + this.rootWrapper.on('mouseout', '[document-node-element], [document-text-element]', hoverHandler); this.eventBus.on('elementToggled', function(toggle, element) { if(!toggle) { @@ -258,7 +271,7 @@ $.extend(Canvas.prototype, Backbone.Events, { }, getCurrentTextElement: function() { - var htmlElement = this.wrapper.find('.current-text-element')[0]; + var htmlElement = this.rootWrapper.find('.current-text-element')[0]; if(htmlElement) { return this.getDocumentElement(htmlElement); } @@ -279,7 +292,7 @@ $.extend(Canvas.prototype, Backbone.Events, { }, contains: function(element) { - return element && element.dom && element.dom.parents().index(this.wrapper) !== -1; + return element && element.dom && element.dom.parents().index(this.rootWrapper) !== -1; }, triggerSelectionChanged: function() { @@ -336,7 +349,7 @@ $.extend(Canvas.prototype, Backbone.Events, { }.bind(this); var _markAsCurrent = function(element) { if(element instanceof documentElement.DocumentTextElement) { - this.wrapper.find('.current-text-element').removeClass('current-text-element'); + this.rootWrapper.find('.current-text-element').removeClass('current-text-element'); element.dom.addClass('current-text-element'); } else { if(this.currentNodeElement) { @@ -355,7 +368,7 @@ $.extend(Canvas.prototype, Backbone.Events, { currentNodeElement = this.getCurrentNodeElement(); if(currentTextElement && !(currentTextElement.sameNode(textElementToLand))) { - this.wrapper.find('.current-text-element').removeClass('current-text-element'); + this.rootWrapper.find('.current-text-element').removeClass('current-text-element'); } if(textElementToLand) { @@ -394,7 +407,7 @@ $.extend(Canvas.prototype, Backbone.Events, { selection.removeAllRanges(); selection.addRange(range); - this.wrapper.focus(); // FF requires this for caret to be put where range colllapses, Chrome doesn't. + this.rootWrapper.focus(); // FF requires this for caret to be put where range colllapses, Chrome doesn't. }, setCursorPosition: function(position) { @@ -404,11 +417,11 @@ $.extend(Canvas.prototype, Backbone.Events, { }, toggleGrid: function() { - this.wrapper.toggleClass('grid-on'); + this.rootWrapper.toggleClass('grid-on'); this.trigger('changed'); }, isGridToggled: function() { - return this.wrapper.hasClass('grid-on'); + return this.rootWrapper.hasClass('grid-on'); } }); diff --git a/src/editor/modules/documentCanvas/canvas/documentElement.js b/src/editor/modules/documentCanvas/canvas/documentElement.js index 570973e..948e8df 100644 --- a/src/editor/modules/documentCanvas/canvas/documentElement.js +++ b/src/editor/modules/documentCanvas/canvas/documentElement.js @@ -43,6 +43,17 @@ $.extend(DocumentElement.prototype, { }.bind(this)); if(_.isFunction(this.onStateChange)) { this.onStateChange(changes); + if(_.isBoolean(changes.active)) { + if(changes.active) { + var ptr = this; + while(ptr && ptr.wlxmlNode.getTagName() === 'span') { + ptr = ptr.parent(); + } + if(ptr && ptr.gutterGroup) { + ptr.gutterGroup.show(); + } + } + } } }, parent: function() { @@ -109,6 +120,16 @@ $.extend(DocumentNodeElement.prototype, { clearWidgets: function() { this.dom.children('.canvas-widgets').empty(); }, + addToGutter: function(view) { + if(!this.gutterGroup) { + this.gutterGroup = this.canvas.gutter.createViewGroup({ + offsetHint: function() { + return this.canvas.getElementOffset(this); + }.bind(this) + }, this); + } + this.gutterGroup.addView(view); + }, handle: function(event) { var method = 'on' + event.type[0].toUpperCase() + event.type.substr(1); if(this[method]) { diff --git a/src/editor/modules/documentCanvas/canvas/gutter.js b/src/editor/modules/documentCanvas/canvas/gutter.js new file mode 100644 index 0000000..a382871 --- /dev/null +++ b/src/editor/modules/documentCanvas/canvas/gutter.js @@ -0,0 +1,117 @@ +define(function(require) { + +'use strict'; + +var $ = require('libs/jquery'), + _ = require('libs/underscore'), + Backbone = require('libs/backbone'), + gutterBoxTemplate = require('libs/text!./gutterBox.html'); + + +var GutterView = function(gutter) { + gutter.on('show', function(group) { + if(this.groupView) { + this.groupView.remove(); + } + this.groupView = new GutterGroupView(this, group); + this.dom.append(this.groupView.dom); + this.groupView.dom.css({top: group.getOffsetHint()}); + this.groupView.show(); + }, this); + this.dom = $('
'); +}; + + +var GutterGroupView = function(gutterView, group) { + this.gutterView = gutterView; + this.group = group; + this.views = []; + + this.dom = $(gutterBoxTemplate); + + this.dom.on('click', function() { + if(!this.dom.hasClass('focused')) { + var canvas = this.group.meta.canvas; + canvas.setCurrentElement(this.group.meta); + } + }.bind(this)); + + this.group.views.forEach(function(view) { + this.onViewAdded(view); + }.bind(this)); + + this.group.on('viewAdded', this.onViewAdded, this); + this.group.on('focusToggled', this.onFocusToggled, this); +}; +$.extend(GutterGroupView.prototype, { + remove: function() { + this.group.off('viewAdded', this.onViewAdded); + this.group.off('offsetChange', this.onOffsetChange); + this.group.off('focusToggled', this.onFocusToggled); + this.dom.detach(); + }, + onViewAdded: function(view) { + this.views.push(view); + this.dom.append(view.dom); + }, + show: function() { + this.dom.addClass('focused'); + this.views.forEach(function(view) { + if(view.onActivated) { + view.onActivated(); + } + }); + } +}); + + + +/// model + +var ViewGroup = function(params, gutter, meta) { + this.gutter = gutter; + this.params = params; + this.meta = meta; + this.view = $(gutterBoxTemplate); + this.views = []; +}; +$.extend(ViewGroup.prototype, Backbone.Events, { + getOffsetHint: function() { + return _.isFunction(this.params.offsetHint) ? this.params.offsetHint() : this.params.offsetHint; + }, + setOffset: function(offset) { + this.trigger('offsetChange', offset); + this._offset = offset; + }, + addView: function(view) { + this.views.push(view); + this.trigger('viewAdded', view); + }, + show: function() { + this.gutter.show(this); + } +}); + + +var Gutter = function() { +}; + +_.extend(Gutter.prototype, Backbone.Events, { + createViewGroup: function(params, meta) { + return new ViewGroup(params, this, meta); + }, + show: function(group) { + this.trigger('show', group); + }, +}); + + +return { + create: function() { + return new Gutter(); + }, + GutterView: GutterView, + GutterGroupView: GutterGroupView +}; + +}); \ No newline at end of file diff --git a/src/editor/modules/documentCanvas/canvas/gutter.less b/src/editor/modules/documentCanvas/canvas/gutter.less new file mode 100644 index 0000000..f34f4eb --- /dev/null +++ b/src/editor/modules/documentCanvas/canvas/gutter.less @@ -0,0 +1,12 @@ +.gutter { + display: table-cell; +} + +.gutter-box { + border: 1px solid #ddd; + width: 100%; + padding: 5px 10px; + position: relative; + background-color: darken(#FFFCB7, 15%); +} + diff --git a/src/editor/modules/documentCanvas/canvas/gutterBox.html b/src/editor/modules/documentCanvas/canvas/gutterBox.html new file mode 100644 index 0000000..7f53e72 --- /dev/null +++ b/src/editor/modules/documentCanvas/canvas/gutterBox.html @@ -0,0 +1 @@ +
diff --git a/src/editor/modules/documentCanvas/documentCanvas.less b/src/editor/modules/documentCanvas/documentCanvas.less index bd39b6a..b2fbd5d 100644 --- a/src/editor/modules/documentCanvas/documentCanvas.less +++ b/src/editor/modules/documentCanvas/documentCanvas.less @@ -1,6 +1,7 @@ @import 'nodes.less'; @import 'canvas/documentElement.less'; @import 'canvas/genericElement.less'; +@import 'canvas/gutter.less'; #rng-module-documentCanvas { height: 100%; @@ -35,6 +36,9 @@ } .canvas-wrapper { + display: table-cell; + vertical-align: top; + width:800px; outline: 0px solid transparent; } diff --git a/src/editor/modules/rng/editingLayout.html b/src/editor/modules/rng/editingLayout.html index 54a29aa..259f0f4 100644 --- a/src/editor/modules/rng/editingLayout.html +++ b/src/editor/modules/rng/editingLayout.html @@ -1,7 +1,5 @@
-
-
\ No newline at end of file diff --git a/src/editor/modules/rng/editingLayout.less b/src/editor/modules/rng/editingLayout.less index 0f5254a..40082a6 100644 --- a/src/editor/modules/rng/editingLayout.less +++ b/src/editor/modules/rng/editingLayout.less @@ -75,7 +75,7 @@ [fnpjs-place="leftColumn"] { left:0; - right: 360px; + right: 0; } [fnpjs-place="rightColumn"] {