From ba9423462f94e933335bc19e15151979dface148 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Tue, 10 Jan 2012 01:55:38 +0530 Subject: [PATCH] blur editing --- source/Ox.UI/js/Form/Ox.ArrayEditable.js | 72 ++++++++++++----- source/Ox.UI/js/Form/Ox.Editable.js | 22 ++++- source/Ox.UI/js/Form/Ox.Filter.js | 8 +- source/Ox.UI/js/Video/Ox.AnnotationPanel.js | 52 +++++++----- .../Ox.UI/js/Video/Ox.SmallVideoTimeline.js | 26 +++--- .../js/Video/Ox.SmallVideoTimelineImage.js | 4 +- source/Ox.UI/js/Video/Ox.VideoEditor.js | 81 ++++++++++++++----- source/Ox.UI/themes/classic/css/classic.css | 6 +- 8 files changed, 180 insertions(+), 91 deletions(-) diff --git a/source/Ox.UI/js/Form/Ox.ArrayEditable.js b/source/Ox.UI/js/Form/Ox.ArrayEditable.js index b20e084d..bd25c07a 100644 --- a/source/Ox.UI/js/Form/Ox.ArrayEditable.js +++ b/source/Ox.UI/js/Form/Ox.ArrayEditable.js @@ -16,6 +16,7 @@ Ox.ArrayEditable = function(options, self) { items: [], position: -1, selected: '', + submitOnBlur: true, type: 'input', width: 256 }) @@ -43,14 +44,26 @@ Ox.ArrayEditable = function(options, self) { var $target = $(e.target), $parent = $target.parent(), position = $parent.data('position'); - !$target.is('.OxInput') && that.gainFocus(); - if ($parent.is('.OxEditableElement')) { - selectItem( - e.metaKey && position == self.selected - ? -1 : $parent.data('position') - ); - } else { - selectNone(); + //ignore clicks while editing + if (!$target.is('.OxInput')) { + if (self.selected > -1) { + //end editing but keep selected if clicked next to a keyword + if (self.editing || self.$items[self.selected].options('editing')) { + self.editing = false; + self.$items[self.selected].options({editing: false}); + //deselect if not editing and not going to select antoher one + } else if (!$parent.is('.OxEditableElement')) { + selectNone(); + } + } + //select if clicked on other editable element + if ($parent.is('.OxEditableElement')) { + selectItem( + e.metaKey && position == self.selected + ? '' : $parent.data('position') + ); + } + that.gainFocus(); } } @@ -103,6 +116,7 @@ Ox.ArrayEditable = function(options, self) { format: function(value) { return value || ' ' }, + submitOnBlur: self.options.submitOnBlur, tooltip: 'Click to select' + ( item.editable ? ', doubleclick to edit' : '' ), @@ -113,13 +127,18 @@ Ox.ArrayEditable = function(options, self) { .addClass(item.id == self.options.selected ? 'OxSelected' : '') .data({position: i}) .bindEvent({ + blur: function(data) { + that.triggerEvent('blur', data); + }, cancel: function(data) { }, edit: function(data) { + self.editing = true; that.triggerEvent('edit', data); }, submit: function(data) { + Ox.Log("AE", "SUBMIT", data); submit(i, data.value); } }) @@ -131,10 +150,17 @@ Ox.ArrayEditable = function(options, self) { self.selected > -1 && selectItem(0); } - function selectItem(position) { - self.selected = position; - self.options.selected = getSelectedId(); - that.selectItem(self.options.selected); + function selectItem(idOrPosition) { + if (Ox.isString(idOrPosition)) { + self.options.selected = idOrPosition; + self.selected = getSelectedPosition(); + } else { + self.selected = idOrPosition; + self.options.selected = getSelectedId(); + } + that.find('.OxSelected').removeClass('OxSelected'); + self.selected > -1 && self.$items[self.selected].addClass('OxSelected'); + that.triggerEvent('select', {id: self.options.selected}); } function selectLast() { @@ -158,9 +184,12 @@ Ox.ArrayEditable = function(options, self) { function submit(position, value) { var item = self.options.items[position]; if (value === '') { + deleteItem(); - } else { - item.value != value && that.triggerEvent('submit', { + } /*else if (item.value === value) { + that.triggerEvent('blur'); + } else*/ { + that.triggerEvent('submit', { id: item.id, value: value }); @@ -172,7 +201,7 @@ Ox.ArrayEditable = function(options, self) { if (key == 'items') { renderItems(); } else if (key == 'selected') { - that.selectItem(value); + selectItem(value); } } @@ -190,17 +219,16 @@ Ox.ArrayEditable = function(options, self) { */ }; - that.editItem = function(position) { - selectItem(position); - editItem(); + that.blurItem = function() { + Ox.Log('AE', 'bI', self.selected); + self.selected > -1 && self.$items[self.selected].options({editing: false}); }; - that.selectItem = function(id) { + that.editItem = function(id) { + Ox.Log('AE', 'editItem', id); self.options.selected = id; self.selected = getSelectedPosition(); - that.find('.OxSelected').removeClass('OxSelected'); - self.selected > -1 && self.$items[self.selected].addClass('OxSelected'); - that.triggerEvent('select', {id: self.options.selected}); + editItem(); }; that.removeItem = function(position) { diff --git a/source/Ox.UI/js/Form/Ox.Editable.js b/source/Ox.UI/js/Form/Ox.Editable.js index 71410771..5a9243ff 100644 --- a/source/Ox.UI/js/Form/Ox.Editable.js +++ b/source/Ox.UI/js/Form/Ox.Editable.js @@ -62,11 +62,15 @@ Ox.Editable = function(options, self) { .appendTo(that); if (self.options.editing) { - self.options.editing = false; // edit will toggle self.options.editing + self.options.editing = false; edit(); } + function blur() { + that.triggerEvent('blur'); + } + function cancel() { self.options.value = self.originalValue; self.$input.value(formatInputValue()).hide(); @@ -139,7 +143,9 @@ Ox.Editable = function(options, self) { submit: submit }) .appendTo(that.$element); - self.options.submitOnBlur && self.$input.bindEvent({blur: submit}); + self.$input.bindEvent({ + blur: self.options.submitOnBlur ? submit : blur + }); self.$input.find('input').css(self.css); } self.$input.options({ @@ -163,7 +169,7 @@ Ox.Editable = function(options, self) { self.$input.focusInput(self.options.type == 'input'); }, 0); that.$tooltip && that.$tooltip.options({title: ''}); - that.triggerEvent('edit', {editing: true}); + that.triggerEvent('edit'); } } @@ -205,7 +211,15 @@ Ox.Editable = function(options, self) { } self.setOption = function(key, value) { - if (key == 'height' || key == 'width') { + if (key == 'editing') { + if (value) { + // edit will toggle self.options.editing + self.options.editing = false; + edit(); + } else { + submit(); + } + } else if (key == 'height' || key == 'width') { var css = {}; css[key] = value + 'px'; self.$test && self.$test.css(css); diff --git a/source/Ox.UI/js/Form/Ox.Filter.js b/source/Ox.UI/js/Form/Ox.Filter.js index baad7f66..8059ce35 100644 --- a/source/Ox.UI/js/Form/Ox.Filter.js +++ b/source/Ox.UI/js/Form/Ox.Filter.js @@ -454,8 +454,10 @@ Ox.Filter = function(options, self) { return Ox.merge([ Ox.Button({ id: 'remove', - title: self.options.query.conditions.length == 1 - ? 'close' : 'remove', + title: self.options.query.conditions.length == 1 ? 'close' : 'remove', + tooltip: self.options.query.conditions.length == 1 ? 'Reset this condition' + : isGroup ? 'Remove this group of conditions' + : 'Remove this condition', type: 'image' }) .css({margin: '0 4px 0 ' + (isGroup ? '292px' : '8px')}) // fixme: 296 is probably correct, but labels seem to be too wide @@ -484,6 +486,7 @@ Ox.Filter = function(options, self) { Ox.Button({ id: 'add', title: 'add', + tooltip: 'Add a condition', type: 'image' }) .css({margin: '0 ' + (subpos == -1 ? '4px' : '0') + ' 0 4px'}) @@ -504,6 +507,7 @@ Ox.Filter = function(options, self) { Ox.Button({ id: 'addgroup', title: 'bracket', + tooltip: 'Add a group of conditions', type: 'image' }) .css({margin: '0 0 0 4px'}) diff --git a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js index 215c3e70..b0880080 100644 --- a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js +++ b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js @@ -84,6 +84,7 @@ Ox.AnnotationPanel = function(options, self) { items: getAnnotations(), selected: self.options.selected, sort: self.sort, + submitOnBlur: false, width: self.options.width, type: self.options.type == 'text' ? 'textarea' : 'input' }) @@ -93,29 +94,27 @@ Ox.AnnotationPanel = function(options, self) { value: data.value || '' }); }, + blur: function() { + that.triggerEvent('blur'); + }, 'delete': function(data) { that.triggerEvent('remove', {id: data.id}); }, - edit: function(data) { - that.triggerEvent('edit', data); + edit: function() { + self.editing = true; + that.triggerEvent('edit'); }, select: selectAnnotation, - submit: editAnnotation + submit: submitAnnotation }); } self.$annotations.appendTo(that.$content); - Ox.print('SOS', self.options.selected); + //Ox.print('SOS', self.options.selected); self.options.selected && setTimeout(function() { selectAnnotation({id: self.options.selected}); }, 0); - function editAnnotation(data) { - var item = Ox.getObjectById(self.options.items, data.id); - item.value = data.value; - that.triggerEvent('submit', item); - } - function getAnnotations() { return self.options.items.filter(function(item) { return self.options.range == 'all' || ( @@ -126,12 +125,13 @@ Ox.AnnotationPanel = function(options, self) { self.options.range == 'position' && item['in'] <= self.options.position && item.out >= self.options.position - ) + ) || self.editing && item.id == self.options.selected }); } function selectAnnotation(data) { var item = Ox.getObjectById(self.options.items, data.id); + self.options.selected = item ? data.id : ''; that.triggerEvent('select', Ox.extend({ id: data.id }, item ? { @@ -141,27 +141,42 @@ Ox.AnnotationPanel = function(options, self) { } : {})); } + function submitAnnotation(data) { + var item = Ox.getObjectById(self.options.items, data.id); + item.value = data.value; + self.editing = false; + that.triggerEvent('submit', item); + } + function togglePanel() { } self.setOption = function(key, value) { + if (['in', 'out'].indexOf(key) > -1 && self.editing) { + var index = Ox.getIndexById(self.options.items, self.options.selected); + self.options.items[index][key] = value; + self.options.items[index].duration = self.options.out - self.options['in']; + } if (key == 'in') { - self.options.range == 'selection' && self.$annotations.options({ + //fixme: array editable should support item updates while editing + self.editing || self.options.range == 'selection' && self.$annotations.options({ items: getAnnotations() }); } else if (key == 'out') { - self.options.range == 'selection' && self.$annotations.options({ + self.editing || self.options.range == 'selection' && self.$annotations.options({ items: getAnnotations() }); } else if (key == 'position') { - self.options.range == 'position' && self.$annotations.options({ + self.editing || self.options.range == 'position' && self.$annotations.options({ items: getAnnotations() }); } else if (key == 'range') { self.$annotations.options({ items: getAnnotations() }); + } else if (key == 'selected') { + self.$annotations.options('selected', value); } else if (key == 'sort') { self.$annotations.options('sort', value); } @@ -174,14 +189,11 @@ Ox.AnnotationPanel = function(options, self) { var pos = 0; self.options.items.splice(pos, 0, item); self.$annotations.addItem(pos, item); - self.$annotations.editItem(pos); + self.$annotations.editItem(item.id); }; - /*@ - deselectItems deselectItems - @*/ - that.deselectItems = function() { - self.$annotations.options('selected', ''); + that.blurItem = function() { + self.$annotations.blurItem(); }; /*@ diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js index c28b807a..889d9198 100644 --- a/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js +++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js @@ -8,13 +8,13 @@ Ox.SmallVideoTimeline = function(options, self) { _offset: 0, // hack for cases where all these position: absolute elements have to go into a float: left disabled: false, duration: 0, - editing: false, find: '', 'in': 0, out: 0, paused: false, results: [], showMilliseconds: 0, + state: 'default', timeline: '', type: 'player', width: 256 @@ -122,11 +122,11 @@ Ox.SmallVideoTimeline = function(options, self) { function getTimelineImage() { return Ox.SmallVideoTimelineImage({ duration: self.options.duration, - editing: self.options.editing, 'in': self.options['in'], out: self.options.out, results: self.options.results, subtitles: self.options.subtitles, + state: self.options.state, timeline: self.options.timeline, width: self.imageWidth, type: self.options.type @@ -206,18 +206,12 @@ Ox.SmallVideoTimeline = function(options, self) { self.setOption = function(key, value) { if (key == 'duration') { - self.$image.options({ - duration: value - }); + self.$image.options({duration: value}); } else if (key == 'in') { - self.$image.options({ - 'in': value - }); + self.$image.options({'in': value}); self.options.type == 'editor' && setPointMarker('in'); } else if (key == 'out') { - self.$image.options({ - out: value - }); + self.$image.options({out: value}); self.options.type == 'editor' && setPointMarker('out'); } else if (key == 'paused') { self.$positionMarker[ @@ -226,13 +220,11 @@ Ox.SmallVideoTimeline = function(options, self) { } else if (key == 'position') { setPositionMarker(); } else if (key == 'results') { - self.$image.options({ - results: value - }); + self.$image.options({results: value}); + } else if (key == 'state') { + self.$image.options({state: value}); } else if (key == 'subtitles') { - self.$image.options({ - subtitles: value - }); + self.$image.options({subtitles: value}); } else if (key == 'width') { setWidth(); } diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js index 8894c91c..f7048d8f 100644 --- a/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js +++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js @@ -145,8 +145,8 @@ Ox.SmallVideoTimelineImage = function(options, self) { ) + 1, top = 0, bottom = height, - rgb = self.options.state == 'editing' ? [[0, 0, 128], [128, 128, 255]] - : self.options.state == 'selected' ? [[0, 128, 0], [128, 255, 128]] + rgb = self.options.state == 'editing' ? [[0, 128, 0], [128, 255, 128]] + : self.options.state == 'selected' ? [[0, 0, 128], [128, 128, 255]] : [[0, 0, 0], [255, 255, 255]]; Ox.loop(left, right, function(x) { Ox.loop(top, bottom, function(y) { diff --git a/source/Ox.UI/js/Video/Ox.VideoEditor.js b/source/Ox.UI/js/Video/Ox.VideoEditor.js index 86e259a8..71de34e4 100644 --- a/source/Ox.UI/js/Video/Ox.VideoEditor.js +++ b/source/Ox.UI/js/Video/Ox.VideoEditor.js @@ -43,7 +43,6 @@ Ox.VideoEditor = function(options, self) { selected: '', showAnnotations: false, showLargeTimeline: true, - state: 'default', subtitles: [], tooltips: false, videoRatio: 16/9, @@ -154,10 +153,11 @@ Ox.VideoEditor = function(options, self) { $player: [], $timeline: [], controlsHeight: 16, + editing: false, margin: 8, }); - Ox.print('VIDEO EDITOR OPTIONS', self.options) + //Ox.print('VIDEO EDITOR OPTIONS', self.options) self.words = []; Ox.forEach(Ox.count(Ox.words(self.options.subtitles.map(function(subtitle) { @@ -173,9 +173,17 @@ Ox.VideoEditor = function(options, self) { self.$editor = Ox.Element() .addClass('OxVideoEditor') - .click(function(e) { + .mousedown(function(e) { var $target = $(e.target); !$target.is('.OxPosition') && !$target.is('input') && that.gainFocus(); + // the following is needed to determine + // how to handle annotation input blur + if (self.editing) { + self.focused = true; + setTimeout(function() { + self.focused = false; + }, 25); + } }); self.sizes = getSizes(); @@ -288,7 +296,7 @@ Ox.VideoEditor = function(options, self) { results: find(self.options.find), showPointMarkers: true, showSubtitles: true, - state: self.options.state, + state: self.options.selected ? 'selected' : 'default', subtitles: self.options.subtitles, videoId: self.options.videoId, width: self.sizes.timeline[1].width @@ -332,8 +340,19 @@ Ox.VideoEditor = function(options, self) { data.out = self.options.out; that.triggerEvent('addannotation', data); }, - edit: function(data) { - setState(data.editing ? 'editing' : 'selected'); + blur: function() { + //Ox.print('VIDEO EDITOR BLUR FOCUSED?', self.focused) + if (self.focused) { + // ... + } else { + self.editing = false; + setState(); + this.blurItem(); + } + }, + edit: function() { + self.editing = true; + setState(); }, remove: function(data) { that.triggerEvent('removeannotation', { @@ -345,7 +364,7 @@ Ox.VideoEditor = function(options, self) { if (data.id) { self.options.layers.forEach(function(layer_, i_) { if (i_ != i) { - self.$annotationPanel[i_].deselectItems(); + self.$annotationPanel[i_].options({selected: ''}); } }); selectAnnotation(data); @@ -738,6 +757,8 @@ Ox.VideoEditor = function(options, self) { }, 0); function editAnnotation(data) { + self.editing = false; + setState(); data['in'] = self.options['in']; data.out = self.options.out; that.triggerEvent('editannotation', data); @@ -816,7 +837,6 @@ Ox.VideoEditor = function(options, self) { } function getSizes(scrollbarIsVisible) { - //Ox.Log('Video', 'getSizes', scrollbarIsVisible) var scrollbarWidth = Ox.UI.SCROLLBAR_SIZE, contentWidth = self.options.width - (self.options.showAnnotations * self.options.annotationsSize) - 1 @@ -828,6 +848,11 @@ Ox.VideoEditor = function(options, self) { timeline: [] }, width, widths; + function getHeight() { + return size.player[0].height + self.controlsHeight + + size.timeline[0].height + lines * 16 + + (lines + 3) * self.margin; + } if (self.options.videoSize == 'small') { width = 0; widths = Ox.divideInt(contentWidth - 4 * self.margin, 3); @@ -876,13 +901,7 @@ Ox.VideoEditor = function(options, self) { self.$editor.css({ overflowY: (scrollbarIsVisible && height <= self.options.height - 16) ? 'scroll' : 'auto' }); - //Ox.Log('Video', 'getSizes', scrollbarIsVisible, height, self.options.height, size) return (!scrollbarIsVisible && height > self.options.height - 16) ? getSizes(true) : size; - function getHeight() { - return size.player[0].height + self.controlsHeight - + size.timeline[0].height + lines * 16 - + (lines + 3) * self.margin; - } } function goToPoint(point) { @@ -935,12 +954,15 @@ Ox.VideoEditor = function(options, self) { } function selectAnnotation(data) { + //Ox.print('VE.sA') + self.editing = false; + self.options.selected = data.id || ''; self.options.annotationsRange != 'position' && setPosition(data['in']); - setState(data.id ? 'selected' : 'default'); + setState(); that.triggerEvent('select', { - id: data.id || '' + id: self.options.selected }); - if (data.id) { + if (self.options.selected) { setPoint('in', data['in']); setPoint('out', data.out); } @@ -954,9 +976,13 @@ Ox.VideoEditor = function(options, self) { function setPoint(point, position, annotation) { var otherPoint = point == 'in' ? 'out' : 'in'; self.options[point] = position; - if (self.options.state == 'selected') { - setState('default'); + /* + // should only happen through interaction + if (self.options.selected && !self.editing) { + self.options.selected = ''; + setState(); } + */ self.$player.forEach(function($player) { $player.options(point, self.options[point]); }); @@ -979,6 +1005,13 @@ Ox.VideoEditor = function(options, self) { 'in': self.options['in'], out: self.options.out }); + if (self.editing) { + that.triggerEvent('editannotation', { + id: self.options.selected, + 'in': self.options['in'], + out: self.options.out + }); + } } function setPosition(position, playing) { @@ -1024,9 +1057,13 @@ Ox.VideoEditor = function(options, self) { }); } - function setState(state) { - self.options.state = state; - self.$timeline[1].options({state: state}); + function setState() { + self.$timeline[1].options({ + state: self.editing ? 'editing' + : self.options.selected ? 'selected' + : 'default' + }); + //Ox.print('SET STATE', self.$timeline[1].options('state')) } function submitFindInput(value, hasPressedEnter) { diff --git a/source/Ox.UI/themes/classic/css/classic.css b/source/Ox.UI/themes/classic/css/classic.css index 0b357655..38d4af1d 100644 --- a/source/Ox.UI/themes/classic/css/classic.css +++ b/source/Ox.UI/themes/classic/css/classic.css @@ -740,10 +740,12 @@ Video } .OxThemeClassic .OxAnnotationPanel .OxEditableElement.OxSelected { - background: rgb(192, 255, 192); + background: rgb(192, 192, 255); + box-shadow: 0 0 1px rgb(96, 96, 96); } .OxThemeClassic .OxAnnotationPanel .OxEditableElement input { - background: rgb(192, 192, 255); + background: rgb(160, 224, 160); + box-shadow: 0 0 1px rgb(96, 96, 96); } /*