From 24263a3e177d42091f578a5c24bdf225a3ae4b5c Mon Sep 17 00:00:00 2001 From: rlx <0x0073@0x2620.org> Date: Sun, 16 Jan 2011 19:02:29 +0000 Subject: [PATCH] better mouse events, fix bug where position would become zero while dragging on small timeline, and add shift-drag ratio-conserving resize to dialogs --- build/js/ox.ui.js | 531 +++++++++++++++++++++++++++++----------------- 1 file changed, 336 insertions(+), 195 deletions(-) diff --git a/build/js/ox.ui.js b/build/js/ox.ui.js index 81189c2b..d433968e 100644 --- a/build/js/ox.ui.js +++ b/build/js/ox.ui.js @@ -940,13 +940,67 @@ requires that.$element = $('<' + (self.options.element || 'div') + '/>', { data: { ox: that.id - } + }, + mousedown: mousedown }); $elements[that.id] = that; wrapjQuery(); })(); // private + + function mousedown(e) { + /* + better mouse events + on mousedown: + trigger mousedown + within 250 msec: + mouseup: trigger anyclick ("click" would collide with click events of certain widgets) + mouseup + mousedown: trigger doubleclick + after 250 msec: + mouseup + no mousedown within 250 msec: trigger singleclick + no mouseup within 250 msec: trigger dragstart + mousemove: trigger drag + mouseup: trigger dragend + */ + if (!self.mouseTimeout) { + // first mousedown + that.triggerEvent('mousedown', e); + self.mouseup = false; + self.mouseTimeout = setTimeout(function() { + self.mouseTimeout = 0; + if (self.mouseup) { + // singleclick + that.triggerEvent('singleclick', e); + } else { + // drag + that.triggerEvent('dragstart', e); + $window.unbind('mouseup', mouseup) + .mousemove(mousemove) + .one('mouseup', function(e) { + $window.unbind('mousemove', mousemove); + that.triggerEvent('dragend', e); + }); + } + }, 250); + } else { + // second mousedown + clearTimeout(self.mouseTimeout); + self.mouseTimeout = 0; + that.triggerEvent('doubleclick'); + } + $window.one('mouseup', mouseup); + function mousemove(e) { + that.triggerEvent('drag', e); + } + function mouseup(e) { + if (!self.mouseup) { // fixme: shouldn't be necessary, bound only once + that.triggerEvent('anyclick', e); + self.mouseup = true; + } + } + } + function wrapjQuery() { $.each(oxui.jQueryFunctions, function(i, fn) { that[fn] = function() { @@ -1108,11 +1162,15 @@ requires */ if (Ox.isObject(arguments[0])) { $.each(arguments[0], function(event, data) { - Ox.print(that.id, self.options.id, 'trigger', event, data); + if (['mousedown', 'anyclick', 'singleclick', 'doubleclick', 'dragstart', 'drag', 'dragend'].indexOf(event) == -1) { + Ox.print(that.id, self.options.id, 'trigger', event, data); + } self.$eventHandler.trigger('ox_' + event, data); }); } else { - Ox.print(that.id, self.options ? self.options.id : '', 'trigger', arguments[0], arguments[1] || {}); + if (['mousedown', 'anyclick', 'singleclick', 'doubleclick', 'dragstart', 'drag', 'dragend'].indexOf(arguments[0]) == -1) { + Ox.print(that.id, self.options ? self.options.id : '', 'trigger', arguments[0], arguments[1] || {}); + } self.$eventHandler.trigger('ox_' + arguments[0], arguments[1] || {}); } return that; @@ -1460,8 +1518,12 @@ requires Ox.print('dragging', e) }) */ - .mousedown(dragStart) - .dblclick(toggle) + .bindEvent({ + doubleclick: toggle, + dragstart: dragstart, + drag: drag, + dragend: dragend + }) .append($('
').addClass('OxSpace')) .append($('
').addClass('OxLine')) .append($('
').addClass('OxSpace')); @@ -1470,16 +1532,23 @@ requires clientXY: self.options.orientation == 'horizontal' ? 'clientY' : 'clientX', dimensions: oxui.getDimensions(self.options.orientation), // fixme: should orientation be the opposite orientation here? edges: oxui.getEdges(self.options.orientation), - leftOrTop: self.options.edge == 'left' || self.options.edge == 'top', - startPos: 0, - startSize: 0 + leftOrTop: self.options.edge == 'left' || self.options.edge == 'top' }); - function drag(e) { - var d = e[self.clientXY] - self.startPos + function dragstart(event, e) { + if (self.options.resizable && !self.options.collapsed) { + self.drag = { + startPos: e[self.clientXY], + startSize: self.options.size + } + } + } + + function drag(event, e) { + var d = e[self.clientXY] - self.drag.startPos size = self.options.size; self.options.size = Ox.limit( - self.startSize + d * (self.leftOrTop ? 1 : -1), + self.drag.startSize + d * (self.leftOrTop ? 1 : -1), self.options.resize[0], self.options.resize[self.options.resize.length - 1] ); @@ -1509,25 +1578,17 @@ requires } } - function dragStart(e) { - if (self.options.resizable && !self.options.collapsed) { - self.startPos = e[self.clientXY]; - self.startSize = self.options.size; - //Ox.print('startSize', self.startSize) - $window.mousemove(drag); - $window.one('mouseup', dragStop); - } - } - - function dragStop() { - self.options.size != self.startSize && triggerEvents('resizeend'); - $window.unbind('mousemove'); + function dragend() { + self.options.size != self.drag.startSize && triggerEvents('resizeend'); } function toggle() { if (self.options.collapsible) { // fixme: silly, pass a parameter - self.options.parent.toggle(self.leftOrTop ? 0 : self.options.parent.options('elements').length - 1); + self.options.parent.toggle( + self.leftOrTop ? 0 : + self.options.parent.options('elements').length - 1 + ); self.options.collapsed = !self.options.collapsed; } /* @@ -1650,8 +1711,11 @@ requires .addClass('OxTitleBar') .appendTo(that); self.options.movable && that.$titlebar - .mousedown(drag) - .dblclick(center); + .dblclick(center) + .bindEvent({ + dragstart: dragstart, + drag: drag + }); that.$title = new Ox.Element() .addClass('OxTitle') @@ -1690,37 +1754,97 @@ requires }); } - function drag(event) { - var bodyWidth = $body.width(), - bodyHeight = $document.height(), - elementWidth = that.width(), - offset = that.offset(), - x = event.clientX, - y = event.clientY; - $window.mousemove(function(event) { - that.css({ - margin: 0 - }); - var left = Ox.limit( - offset.left - x + event.clientX, - 24 - elementWidth, bodyWidth - 24 - //0, documentWidth - elementWidth - ), - top = Ox.limit( - offset.top - y + event.clientY, - 24, bodyHeight - 24 - //24, documentHeight - elementHeight - ); - that.css({ - left: left + 'px', - top: top + 'px' - }); + function dragstart(event, e) { + self.drag = { + bodyWidth: $body.width(), + bodyHeight: $document.height(), + elementWidth: that.width(), + offset: that.offset(), + x: e.clientX, + y: e.clientY + }; + that.css({ + margin: 0 }); - $window.one('mouseup', function() { - $window.unbind('mousemove'); + } + + function drag(event, e) { + var left = Ox.limit( + self.drag.offset.left - self.drag.x + e.clientX, + 24 - self.drag.elementWidth, self.drag.bodyWidth - 24 + //0, self.drag.documentWidth - self.drag.elementWidth + ), + top = Ox.limit( + self.drag.offset.top - self.drag.y + e.clientY, + 24, self.drag.bodyHeight - 24 + //24, self.drag.documentHeight - self.drag.elementHeight + ); + that.css({ + left: left + 'px', + top: top + 'px' }); } + function dragstartResize(event, e) { + self.drag = { + documentWidth: $document.width(), + documentHeight: $document.height(), + elementWidth: that.width(), + elementHeight: that.height(), + offset: that.offset(), + x: e.clientX, + y: e.clientY + }; + $.extend(self.drag, { + ratio: self.drag.elementWidth / self.drag.elementHeight + }); + that.css({ + left: self.drag.offset.left, + top: self.drag.offset.top, + margin: 0 + }); + } + + function dragResize(event, e) { + if (!e.shiftKey) { + self.drag.ratio = self.options.width / self.options.height; + } + self.options.width = Ox.limit( + self.drag.elementWidth - self.drag.x + e.clientX, + self.options.minWidth, + Math.min( + self.drag.documentWidth, + self.drag.documentWidth - self.drag.offset.left + ) + ); + self.options.height = Ox.limit( + self.drag.elementHeight - self.drag.y + e.clientY, + self.options.minHeight, + Math.min( + self.drag.documentHeight, + self.drag.documentHeight - self.drag.offset.top + ) + ); + if (e.shiftKey) { + self.options.height = Ox.limit( + self.options.width / self.drag.ratio, + self.options.minHeight, + Math.min( + self.drag.documentHeight, + self.drag.documentHeight - self.drag.offset.top + ) + ); + self.options.width = self.options.height * self.drag.ratio; + } + that.width(self.options.width); + that.height(self.options.height); + that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically + } + + function dragendResize(event, e) { + triggerResizeEvent(); + } + function getButtonById(id) { var ret = null; //Ox.print('that.$buttons', that.$buttons, id) @@ -1764,8 +1888,12 @@ requires if (self.options.resizable) { that.$resize = new Ox.Element() .addClass('OxResize') - .mousedown(resize) .dblclick(reset) + .bindEvent({ + dragstart: dragstartResize, + drag: dragResize, + dragend: dragendResize + }) .appendTo(that.$buttonsbar); } //Ox.print('--- two', self.options.buttons[1]); @@ -1803,38 +1931,6 @@ requires triggerResizeEvent(); } - function resize(event) { // fixme: reserved jquery string? - var documentWidth = $document.width(), - documentHeight = $document.height(), - elementWidth = that.width(), - elementHeight = that.height(), - offset = that.offset(), - x = event.clientX, - y = event.clientY; - $window.mousemove(function(event) { - that.css({ - left: offset.left, - top: offset.top, - margin: 0 - }); - self.options.width = Ox.limit( - elementWidth - x + event.clientX, - self.options.minWidth, Math.min(documentWidth, documentWidth - offset.left) - ); - self.options.height = Ox.limit( - elementHeight - y + event.clientY, - self.options.minHeight, documentHeight - offset.top - ); - that.width(self.options.width); - that.height(self.options.height); - that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically - triggerResizeEvent(); - }); - $window.one('mouseup', function() { - $window.unbind('mousemove'); - }); - } - function triggerResizeEvent() { that.triggerEvent('resize', { width: self.options.width, @@ -7561,27 +7657,28 @@ requires that.$titles = []; self.columnOffsets = []; $.each(self.visibleColumns, function(i, v) { - var $order, $resize, $left, $center, $right, timeout = 0; + var $order, $resize, $left, $center, $right; offset += self.columnWidths[i]; self.columnOffsets[i] = offset - self.columnWidths[i] / 2; - that.$titles[i] = $('
') + that.$titles[i] = new Ox.Element() .addClass('OxTitle OxColumn' + Ox.toTitleCase(v.id)) .css({ width: (self.columnWidths[i] - 9) + 'px', textAlign: v.align }) .html(v.title) - .mousedown(function(e) { - timeout = setTimeout(function() { - self.options.columnsMovable && dragColumn(v.id, e); - timeout = 0; - }, 250); - }) - .mouseup(function() { - if (timeout) { - clearTimeout(timeout); - timeout = 0; + .bindEvent({ + anyclick: function(event, e) { clickColumn(v.id); + }, + dragstart: function(event, e) { + dragstartColumn(v.id, e); + }, + drag: function(event, e) { + dragColumn(v.id, e); + }, + dragend: function(event, e) { + dragendColumn(v.id, e); } }) .appendTo(that.$head.$content.$element); @@ -7594,11 +7691,28 @@ requires $(this).prev().trigger('click') }) .appendTo(that.$head.$content.$element); - $resize = $('
') + Ox.print('okok') + $resize = new Ox.Element() .addClass('OxResize') .appendTo(that.$head.$content.$element); + Ox.print('nono') if (self.options.columnsResizable) { $resize.addClass('OxResizable') + .bindEvent({ + doubleclick: function(event, e) { + resetColumn(v.id, e); + }, + dragstart: function(event, e) { + dragstartResize(v.id, e); + }, + drag: function(event, e) { + dragResize(v.id, e); + }, + dragend: function(event, e) { + dragendResize(v.id, e); + } + }); + /* // fixme: make these their own named functions .mousedown(function(e) { var startWidth = self.columnWidths[i], @@ -7634,10 +7748,11 @@ requires width: width }); }); + */ } - $left = $('
').addClass('OxLeft').appendTo($resize); - $center = $('
').addClass('OxCenter').appendTo($resize); - $right = $('
').addClass('OxRight').appendTo($resize); + $left = $('
').addClass('OxLeft').appendTo($resize.$element); + $center = $('
').addClass('OxCenter').appendTo($resize.$element); + $right = $('
').addClass('OxRight').appendTo($resize.$element); }); that.$head.$content.css({ width: (Ox.sum(self.columnWidths) + 2) + 'px' @@ -7689,64 +7804,83 @@ requires return $item; } - function dragColumn(id, e) { - var startX = e.clientX, - startPos = getColumnPositionById(id), - pos = startPos, - stopPos = startPos, - offsets = $.map(self.visibleColumns, function(v, i) { - return self.columnOffsets[i] - self.columnOffsets[startPos] - }); + function dragstartColumn(id, e) { + self.drag = { + startX: e.clientX, + startPos: getColumnPositionById(id) + } + $.extend(self.drag, { + stopPos: self.drag.startPos, + offsets: $.map(self.visibleColumns, function(v, i) { + return self.columnOffsets[i] - self.columnOffsets[self.drag.startPos] + }) + }); $('.OxColumn' + Ox.toTitleCase(id)).css({ opacity: 0.25 }); - that.$titles[startPos].addClass('OxDrag').css({ // fixme: why does the class not work? + that.$titles[self.drag.startPos].addClass('OxDrag').css({ // fixme: why does the class not work? cursor: 'move' }); - //Ox.print('offsets', offsets) - $window.mousemove(function(e) { - var d = e.clientX - startX; - $.each(offsets, function(i, v) { - if (d < 0 && d < v) { - stopPos = i; - return false; - } else if (d > 0 && d > v) { - stopPos = i; - } - }); - if (stopPos != pos) { - pos = stopPos; - moveColumn(id, pos); - } - }); - $window.one('mouseup', function() { - dropColumn(id, pos); - $window.unbind('mousemove'); - }); } - function dropColumn(id, pos) { - //Ox.print('dropColumn', id, pos) - var startPos = getColumnPositionById(id), - stopPos = pos, - $title = that.$titles.splice(startPos, 1)[0], - column = self.visibleColumns.splice(startPos, 1)[0], - width = self.columnWidths.splice(startPos, 1)[0]; - self.visibleColumns.splice(stopPos, 0, column); - self.columnWidths.splice(stopPos, 0, width); + function dragColumn(id, e) { + var d = e.clientX - self.drag.startX, + pos = self.drag.stopPos; + $.each(self.drag.offsets, function(i, v) { + if (d < 0 && d < v) { + self.drag.stopPos = i; + return false; + } else if (d > 0 && d > v) { + self.drag.stopPos = i; + } + }); + if (self.drag.stopPos != pos) { + moveColumn(id, self.drag.stopPos); + } + } + + function dragendColumn(id, e) { + var column = self.visibleColumns.splice(self.drag.stopPos, 1)[0], + width = self.columnWidths.splice(self.drag.stopPos, 1)[0]; + self.visibleColumns.splice(self.drag.stopPos, 0, column); + self.columnWidths.splice(self.drag.stopPos, 0, width); that.$head.$content.empty(); constructHead(); - //Ox.print('s.vC', self.visibleColumns) $('.OxColumn' + Ox.toTitleCase(id)).css({ opacity: 1 }); - that.$titles[stopPos].removeClass('OxDrag').css({ + that.$titles[self.drag.stopPos].removeClass('OxDrag').css({ cursor: 'pointer' }); that.$body.clearCache(); triggerColumnChangeEvent(); } + function dragstartResize(id, e) { + var pos = getColumnPositionById(id); + self.drag = { + startX: e.clientX, + startWidth: self.columnWidths[pos] + }; + } + + function dragResize(id, e) { + var width = Ox.limit( + self.drag.startWidth - self.drag.startX + e.clientX, + self.options.columnWidth[0], + self.options.columnWidth[1] + ); + resizeColumn(id, width); + } + + function dragendResize(id, e) { + var pos = getColumnPositionById(id); + that.triggerEvent('columnresize', { + id: id, + width: self.columnWidths[pos] + }); + } + function getCell(id, key) { Ox.print('getCell', id, key) var $item = getItem(id); @@ -7841,8 +7975,13 @@ requires //that.$body.clearCache(); } - function resize() { - + function resetColumn(id) { + var width = self.defaultColumnWidths[getColumnIndexById(id)]; + resizeColumn(id, width); + that.triggerEvent('columnresize', { + id: id, + width: width + }); } function resizeColumn(id, width) { @@ -7862,7 +8001,6 @@ requires width: (width - (self.options.columnsVisible ? 9 : 8)) + 'px' }); setWidth(); - //that.$body.clearCache(); } function setWidth() { @@ -10011,9 +10149,13 @@ requires }) .options(options || {}) .addClass('OxTimelineLarge') - .mousedown(mousedown) .mouseleave(mouseleave) - .mousemove(mousemove); + .mousemove(mousemove) + .bindEvent({ + anyclick: click, + dragstart: dragstart, + drag: drag + }); $.extend(self, { $cuts: [], @@ -10079,35 +10221,27 @@ requires setWidth(); setPosition(); - function mousedown(e) { - var mousemove = false, - x = e.clientX; - $window.mousemove(function(e) { - mousemove = true; - self.options.position = Ox.limit( - self.options.position + (x - e.clientX) / self.fps, - 0, self.options.duration - ); - x = e.clientX; - setPosition(); - that.triggerEvent('change', { - position: self.options.position - }); - }); - $window.one('mouseup', function() { - $window.unbind('mousemove'); - if (!mousemove) { - self.options.position = Ox.limit( - self.options.position + (e.clientX - that.$element.offset().left - self.center) / self.fps, - 0, self.options.duration - ); - setPosition(); - } - that.triggerEvent('change', { - position: self.options.position - }); - }); - e.preventDefault(); + function click(event, e) { + self.options.position = Ox.limit( + self.options.position + (e.clientX - that.$element.offset().left - self.center) / self.fps, + 0, self.options.duration + ); + setPosition(); + triggerChangeEvent(); + } + + function dragstart(event, e) { + self.drag = {x: e.clientX}; + } + + function drag(event, e) { + self.options.position = Ox.limit( + self.options.position + (self.drag.x - e.clientX) / self.fps, + 0, self.options.duration + ); + self.drag.x = e.clientX; + setPosition(); + triggerChangeEvent(); } function mouseleave(e) { @@ -10168,6 +10302,12 @@ requires setMarker(); } + function triggerChangeEvent() { + that.triggerEvent('change', { + position: self.options.position + }); + } + function updateTooltip() { var position = self.options.position + (self.clientX - that.offset().left - self.center) / self.fps; if (position >= 0 && position <= self.options.duration) { @@ -10217,7 +10357,12 @@ requires .addClass('OxTimelineSmall') .mousedown(mousedown) .mouseleave(mouseleave) - .mousemove(mousemove); + .mousemove(mousemove) + .bindEvent({ + drag: function(event, e) { + mousedown(e); + } + }); $.extend(self, { $images: [], @@ -10290,6 +10435,7 @@ requires if (self.hasSubtitles) { self.subtitlesImageURL = getSubtitlesImageURL(); self.$subtitles[i] = $('') + .addClass('OxTimelineSmallSubtitles') .attr({ src: self.subtitlesImageURL }) @@ -10376,41 +10522,34 @@ requires } function mousedown(e) { - if ($(e.target).is('img')) { + var $target = $(e.target); + if ( + $target.hasClass('OxTimelineSmallImage') || + $target.hasClass('OxTimelineSmallSubtitles') + ) { self.options.position = getPosition(e); setPosition(); that.triggerEvent('change', { position: self.options.position }); } - $window.mousemove(function(e) { - if ($(e.target).is('img')) { - self.options.position = getPosition(e); - setPosition(); - that.triggerEvent('change', { - position: self.options.position - }); - } - }); - $window.one('mouseup', function() { - $window.unbind('mousemove'); - }) e.preventDefault(); } function mouseleave(e) { - self.$tooltip.hide(); + self.$tooltip && self.$tooltip.hide(); } function mousemove(e) { var $target = $(e.target), position, subtitle; - if ($target.is('img')) { - //FIXME: this might still be broken in opera according to http://acko.net/blog/mouse-handling-and-absolute-positions-in-javascript + if ( + $target.hasClass('OxTimelineSmallImage') || + $target.hasClass('OxTimelineSmallSubtitles') + ) { position = getPosition(e), subtitle = getSubtitle(position); - //Ox.print('position', position, e) self.$tooltip = new Ox.Tooltip({ title: subtitle ? '' + @@ -10422,6 +10561,8 @@ requires textAlign: 'center' }) .show(e.clientX, e.clientY); + } else { + self.$tooltip && self.$tooltip.hide(); } }