aboutsummaryrefslogtreecommitdiffstats
path: root/front/dragging.ts
blob: bcf6e9f5461ffaa0b3ede914d4be89a816355de1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

export class Draggable {
    el: HTMLElement;

    reparent: boolean = true;

    lockSize: boolean = true;

    onBeforeDragStart: () => void = null;
    onAfterDragEnd:    () => void = null;
    customMoveHandler: (x: number, y: number) => void = null;

    constructor(el: HTMLElement) {
        this.el = el;
    }
}




// If we're currently dragging something (a window or a file), this is the DOM object we're dragging
// dragging_offset_x and dragging_offset_y are the difference between the objec's top left point and the cursor position
// this is then added to the cursor position when the mouse is moved
export var dragging: Draggable = null;
export var dragging_offset_x = 0, dragging_offset_y = 0;

// If we have pressed down a window's title bar but haven't yet started dragging it, it's the drag candidate
// This is needed because we don't yet know whether we want to start dragging the window or click an element in the titlebar
// Once the mouse has moved sufficiently far away from dragging_candidate_x or y, we start dragging
export var dragging_candidate: Draggable = null;
export var dragging_candidate_x, dragging_candidate_y;

export function set_dragging_candidate(e, candidate: Draggable) {
    dragging_candidate = candidate;
    dragging_candidate_x = e.clientX;
    dragging_candidate_y = e.clientY;
}


// Start dragging the 'obj' DOM element
// e is a DOM event, this should only get called in response of a DOM event
export function begin_drag(e, d: Draggable) {
    if (d.onBeforeDragStart)
        d.onBeforeDragStart()
    set_iframe_enabled(false);
    dragging = d;
    dragging_candidate = null;
    dragging.el.classList.add("dragged");

    var elemRect = dragging.el.getBoundingClientRect();
    dragging_offset_x = e.clientX - elemRect.left;
    dragging_offset_y = -e.clientY + elemRect.top;


    if (dragging.lockSize) {
        dragging.el.style.left = (e.clientX - dragging_offset_x) + "px";
        dragging.el.style.top  = (e.clientY + dragging_offset_y) + "px";
        dragging.el.style.width  = elemRect.width  + "px";
        dragging.el.style.height = elemRect.height + "px";
        dragging.el.style.position = "absolute";
    }



    if (dragging.reparent)
        document.body.appendChild(dragging.el);
}

export function end_drag(_e) {
    set_iframe_enabled(true);

    dragging.el.classList.remove("dragged");
    if (dragging.onAfterDragEnd)
        dragging.onAfterDragEnd();
    dragging = null;
}

function set_iframe_enabled(en) {
    const frames = document.getElementsByTagName('iframe');
    for (var i = 0; i < frames.length; i++)
        frames.item(i).hidden = !en;
}

document.body.onmouseup = (_e) => {
    if (dragging_candidate)
        dragging_candidate = null;
    if (dragging)
        end_drag(_e);
}

document.body.onmousemove = (e) => {
    if (dragging) {
        const x = e.clientX - dragging_offset_x;
        const y = e.clientY + dragging_offset_y;
        if (dragging.customMoveHandler) {
            dragging.customMoveHandler(x, y)
        } else {
            dragging.el.style.left = x + "px";
            dragging.el.style.top  = y + "px";
        }
    }
    else if (dragging_candidate) {
        var d = Math.abs(e.clientX - dragging_candidate_x) + Math.abs(e.clientY - dragging_candidate_y);
        if (d > 15)
            begin_drag(e, dragging_candidate);
    }
}

export function oncontextmenu_hook(e) {
    if (dragging) {
        end_drag(e);
        e.preventDefault();
    }
}