284 lines
9.2 KiB
JavaScript
284 lines
9.2 KiB
JavaScript
|
/*
|
||
|
* This file is part of Toolkit.
|
||
|
*
|
||
|
* Toolkit is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 3 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* Toolkit is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General
|
||
|
* Public License along with this program; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
"use strict";
|
||
|
(function(w, TK){
|
||
|
|
||
|
function start_drag(value) {
|
||
|
if (!value) return;
|
||
|
var O = this.options;
|
||
|
this.start_pos = O.range.call(this).val2px(O.get.call(this));
|
||
|
this.fire_event("startdrag", this.drag_state.start);
|
||
|
if (O.events) O.events.call(this).fire_event("startdrag", this.drag_state.start);
|
||
|
}
|
||
|
|
||
|
/* This version integrates movements, instead
|
||
|
* of using the global change since the beginning */
|
||
|
function movecapture_int(O, range, state) {
|
||
|
/* O.direction is always 'polar' here */
|
||
|
|
||
|
/* movement since last event */
|
||
|
var v = state.prev_distance();
|
||
|
var RO = range.options;
|
||
|
|
||
|
if (!v[0] && !v[1]) return;
|
||
|
|
||
|
var V = O._direction;
|
||
|
|
||
|
var dist = Math.sqrt(v[0]*v[0] + v[1]*v[1]);
|
||
|
|
||
|
var c = (V[0]*v[0] - V[1]*v[1]) / dist;
|
||
|
|
||
|
if (Math.abs(c) > O._cutoff) return;
|
||
|
|
||
|
if (v[0] * V[1] + v[1] * V[0] < 0) dist = -dist;
|
||
|
|
||
|
var multi = RO.step || 1;
|
||
|
var e = state.current;
|
||
|
|
||
|
if (e.ctrlKey || e.altKey) {
|
||
|
multi *= RO.shift_down;
|
||
|
} else if (e.shiftKey) {
|
||
|
multi *= RO.shift_up;
|
||
|
}
|
||
|
|
||
|
dist *= multi;
|
||
|
var v = this.start_pos + dist;
|
||
|
|
||
|
var nval = range.px2val(v);
|
||
|
if (O.limit)
|
||
|
O.set.call(this, Math.min(RO.max, Math.max(RO.min, nval)));
|
||
|
else
|
||
|
O.set.call(this, nval);
|
||
|
|
||
|
if (!(nval > RO.min) || !(nval < RO.max)) return;
|
||
|
|
||
|
this.start_pos = v;
|
||
|
}
|
||
|
|
||
|
function movecapture_abs(O, range, state) {
|
||
|
var dist;
|
||
|
var RO = range.options
|
||
|
switch(O.direction) {
|
||
|
case "vertical":
|
||
|
dist = -state.vdistance()[1];
|
||
|
break;
|
||
|
default:
|
||
|
TK.warn("Unsupported direction:", O.direction);
|
||
|
case "horizontal":
|
||
|
dist = state.vdistance()[0];
|
||
|
break;
|
||
|
}
|
||
|
if (O.reverse)
|
||
|
dist *= -1;
|
||
|
|
||
|
var multi = RO.step || 1;
|
||
|
var e = state.current;
|
||
|
|
||
|
if (e.ctrlKey && e.shiftKey) {
|
||
|
multi *= RO.shift_down;
|
||
|
} else if (e.shiftKey) {
|
||
|
multi *= RO.shift_up;
|
||
|
}
|
||
|
|
||
|
dist *= multi;
|
||
|
|
||
|
var nval = range.px2val(this.start_pos + dist);
|
||
|
|
||
|
if (O.limit)
|
||
|
O.set.call(this, Math.min(RO.max, Math.max(RO.min, nval)));
|
||
|
else
|
||
|
O.set.call(this, nval);
|
||
|
}
|
||
|
|
||
|
function movecapture(state) {
|
||
|
var O = this.options;
|
||
|
|
||
|
if (O.active === false) return false;
|
||
|
|
||
|
var state = this.drag_state;
|
||
|
var range = O.range.call(this);
|
||
|
|
||
|
if (O.direction === "polar") {
|
||
|
movecapture_int.call(this, O, range, state);
|
||
|
} else {
|
||
|
movecapture_abs.call(this, O, range, state);
|
||
|
}
|
||
|
|
||
|
this.fire_event("dragging", state.current);
|
||
|
if (O.events) O.events.call(this).fire_event("dragging", state.current);
|
||
|
}
|
||
|
|
||
|
function stop_drag(state, ev) {
|
||
|
this.fire_event("stopdrag", ev);
|
||
|
var O = this.options;
|
||
|
if (O.events) O.events.call(this).fire_event("stopdrag", ev);
|
||
|
}
|
||
|
|
||
|
function angle_diff(a, b) {
|
||
|
// returns an unsigned difference between two angles
|
||
|
var d = (Math.abs(a - b) + 360) % 360;
|
||
|
return d > 180 ? 360 - d : d;
|
||
|
}
|
||
|
TK.DragValue = TK.class({
|
||
|
/**
|
||
|
* TK.DragValue enables dragging an element and setting a
|
||
|
* value according to the dragged distance. TK.DragValue is for example
|
||
|
* used in {@link TK.Knob} and {@link TK.ValueButton}.
|
||
|
*
|
||
|
* @class TK.DragValue
|
||
|
*
|
||
|
* @param {Object} [options={ }] - An object containing initial options.
|
||
|
*
|
||
|
* @property {Element} options.node - The DOM node used for dragging.
|
||
|
* All DOM events are registered with this Element.
|
||
|
* @property {Function} [options.range] - A function returning a
|
||
|
* {@link TK.Range} object for
|
||
|
* calculating the value. Returns its parent (usually having
|
||
|
* {@link TK.Ranged}-features) by default.
|
||
|
* @property {Function} [options.events] - Returns an element firing the
|
||
|
* events <code>startdrag</code>, <code>dragging</code> and <code>stopdrag</code>.
|
||
|
* By default it returns <code>this.parent</code>.
|
||
|
* @property {Element|boolean} [options.classes=false] - While dragging, the class
|
||
|
* <code>toolkit-dragging</code> will be added to this element. If set to <code>false</code>
|
||
|
* the class will be set on <code>options.node</code>.
|
||
|
* @property {Function} [options.get] - Callback function returning the value to drag.
|
||
|
* By default it returns <code>this.parent.options.value</code>.
|
||
|
* @property {Function} [options.set] - Callback function for setting the value.
|
||
|
* By default it calls <code>this.parent.userset("value", [value]);</code>.
|
||
|
* @property {String} [options.direction="polar"] - Direction for changing the value.
|
||
|
* Can be <code>polar</code>, <code>vertical</code> or <code>horizontal</code>.
|
||
|
* @property {Boolean} [options.active=true] - If false, dragging is deactivated.
|
||
|
* @property {Boolean} [options.cursor=false] - If true, a {@link TK.GlobalCursor} is set while dragging.
|
||
|
* @property {Number} [options.blind_angle=20] - If options.direction is <code>polar</code>,
|
||
|
* this is the angle of separation between positive and negative value changes
|
||
|
* @property {Number} [options.rotation=45] - Defines the angle of the center of the positive value
|
||
|
* changes. 0 means straight upward. For instance, a value of 45 leads to increasing value when
|
||
|
* moving towards top and right.
|
||
|
* @property {Boolean} [options.reverse=false] - If true, the difference of pointer travel is inverted.
|
||
|
* @property {Boolean} [options.limit=false] - Limit the returned value to min and max of the range.
|
||
|
*
|
||
|
* @extends TK.Module
|
||
|
*
|
||
|
* @mixes TK.GlobalCursor
|
||
|
*/
|
||
|
_class: "DragValue",
|
||
|
Extends: TK.DragCapture,
|
||
|
Implements: TK.GlobalCursor,
|
||
|
_options: {
|
||
|
get: "function",
|
||
|
set: "function",
|
||
|
range: "function",
|
||
|
events: "function",
|
||
|
classes: "object|boolean",
|
||
|
direction: "string",
|
||
|
active: "boolean",
|
||
|
cursor: "boolean",
|
||
|
blind_angle: "number",
|
||
|
rotation: "number",
|
||
|
reverse: "boolean",
|
||
|
limit: "boolean",
|
||
|
},
|
||
|
options: {
|
||
|
range: function () { return this.parent; },
|
||
|
classes: false,
|
||
|
get: function () { return this.parent.options.value; },
|
||
|
set: function (v) { this.parent.userset("value", v); },
|
||
|
events: function () { return this.parent; },
|
||
|
direction: "polar",
|
||
|
active: true,
|
||
|
cursor: false,
|
||
|
blind_angle: 20,
|
||
|
rotation: 45,
|
||
|
reverse: false,
|
||
|
limit: false,
|
||
|
},
|
||
|
/**
|
||
|
* Is fired while a user is dragging.
|
||
|
*
|
||
|
* @event TK.DragValue#dragging
|
||
|
*
|
||
|
* @param {DOMEvent} event - The native DOM event.
|
||
|
*/
|
||
|
/**
|
||
|
* Is fired when a user starts dragging.
|
||
|
*
|
||
|
* @event TK.DragValue#startdrag
|
||
|
*
|
||
|
* @param {DOMEvent} event - The native DOM event.
|
||
|
*/
|
||
|
/**
|
||
|
* Is fired when a user stops dragging.
|
||
|
*
|
||
|
* @event TK.DragValue#stopdrag
|
||
|
*
|
||
|
* @param {DOMEvent} event - The native DOM event.
|
||
|
*/
|
||
|
static_events: {
|
||
|
set_state: start_drag,
|
||
|
stopcapture: stop_drag,
|
||
|
startcapture: function() {
|
||
|
if (this.options.active) return true;
|
||
|
},
|
||
|
set_rotation: function(v) {
|
||
|
v *= Math.PI / 180;
|
||
|
this.set("_direction", [ -Math.sin(v), Math.cos(v) ]);
|
||
|
},
|
||
|
set_blind_angle: function(v) {
|
||
|
v *= Math.PI / 360;
|
||
|
this.set("_cutoff", Math.cos(v));
|
||
|
},
|
||
|
movecapture: movecapture,
|
||
|
startdrag: function(ev) {
|
||
|
TK.S.add(function() {
|
||
|
var O = this.options;
|
||
|
TK.add_class(O.classes || O.node, "toolkit-dragging");
|
||
|
if (O.cursor) {
|
||
|
if (O.direction === "vertical") {
|
||
|
this.global_cursor("row-resize");
|
||
|
} else {
|
||
|
this.global_cursor("col-resize");
|
||
|
}
|
||
|
}
|
||
|
}.bind(this), 1);
|
||
|
},
|
||
|
stopdrag: function() {
|
||
|
TK.S.add(function() {
|
||
|
var O = this.options;
|
||
|
TK.remove_class(O.classes || O.node, "toolkit-dragging");
|
||
|
|
||
|
if (O.cursor) {
|
||
|
if (O.direction === "vertical") {
|
||
|
this.remove_cursor("row-resize");
|
||
|
} else {
|
||
|
this.remove_cursor("col-resize");
|
||
|
}
|
||
|
}
|
||
|
}.bind(this), 1);
|
||
|
},
|
||
|
},
|
||
|
initialize: function (widget, options) {
|
||
|
TK.DragCapture.prototype.initialize.call(this, widget, options);
|
||
|
this.start_pos = 0;
|
||
|
var O = this.options;
|
||
|
this.set("rotation", O.rotation);
|
||
|
this.set("blind_angle", O.blind_angle);
|
||
|
},
|
||
|
});
|
||
|
})(this, this.TK);
|