Skip to content

Commit cc2adf0

Browse files
committed
https://livecode.ado.me.uk/Home/Session/5cab7518-d459-4a44-9b8e-ac940a5e0268
pointers
1 parent ce69e45 commit cc2adf0

File tree

7 files changed

+238
-60
lines changed

7 files changed

+238
-60
lines changed

LiveCode/Hubs/SessionHub.cs

+48
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,36 @@ public async Task Join(Guid sessionId)
2121
MembershipIndex.TryAdd(Context.ConnectionId, sessionId);
2222

2323
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveContent", SessionContent.GetOrAdd(sessionId, ""), 0, 0);
24+
25+
foreach (string connection in Sessions[sessionId])
26+
{
27+
if (connection == Context.ConnectionId)
28+
{
29+
continue;
30+
}
31+
32+
await Clients.Client(connection).SendAsync("Connected", Context.ConnectionId);
33+
}
34+
}
35+
36+
public override Task OnDisconnectedAsync(Exception exception)
37+
{
38+
if (!MembershipIndex.TryGetValue(Context.ConnectionId, out Guid session))
39+
{
40+
return base.OnDisconnectedAsync(exception);
41+
}
42+
43+
foreach (string connection in Sessions[session])
44+
{
45+
if (connection == Context.ConnectionId)
46+
{
47+
continue;
48+
}
49+
50+
Clients.Client(connection).SendAsync("Disconnected", Context.ConnectionId);
51+
}
52+
53+
return base.OnDisconnectedAsync(exception);
2454
}
2555

2656
public async Task UpdateContent(string content, int row, int pos)
@@ -61,6 +91,24 @@ public async Task UpdateCursor(int row, int pos)
6191
}
6292
}
6393

94+
public async Task UpdatePointer(int x, int y)
95+
{
96+
if (!MembershipIndex.TryGetValue(Context.ConnectionId, out Guid session))
97+
{
98+
return;
99+
}
100+
101+
foreach (string connection in Sessions[session])
102+
{
103+
if (connection == Context.ConnectionId)
104+
{
105+
continue;
106+
}
107+
108+
await Clients.Client(connection).SendAsync("ReceivePointer", Context.ConnectionId, x, y);
109+
}
110+
}
111+
64112
public async Task UpdateSelection(int srow, int spos, int erow, int epos)
65113
{
66114
if (!MembershipIndex.TryGetValue(Context.ConnectionId, out Guid session))

LiveCode/LiveCode.csproj.user

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<PropertyGroup>
44
<ActiveDebugProfile>LiveCode</ActiveDebugProfile>
5+
<ShowAllFiles>true</ShowAllFiles>
56
</PropertyGroup>
67
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
78
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>

LiveCode/Views/Home/Session.cshtml

+16-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@
77

88
</head>
99
<body>
10+
<style>
11+
12+
html, body
13+
{
14+
overflow: hidden;
15+
}
16+
17+
.pointer
18+
{
19+
width: 19px;
20+
height: 32px;
21+
position: absolute;
22+
background-image: url('/css/cursor.png');
23+
z-index: 100;
24+
}
25+
</style>
1026

1127
<script>
1228
SESSION_ID = '@this.ViewBag.SessionId';
@@ -16,7 +32,6 @@
1632

1733
</div>
1834

19-
2035
<script src="~/js/Ace/ace.js"></script>
2136
<script src="~/js/Ace/theme-textmate.js"></script>
2237
<script src="~/js/Ace/mode-csharp.js"></script>

LiveCode/aws-ecs-tools-defaults.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"region" : "us-east-1",
44
"profile" : "Aclor-Cli",
55
"configuration" : "Release",
6-
"image-tag" : "livecode:v2",
6+
"image-tag" : "livecode:v3",
77
"docker-build-working-dir" : "",
88
"vstoolkit-deployment-mode" : "PushOnly"
99
}

LiveCode/wwwroot/css/cursor.png

1.19 KB
Loading

LiveCode/wwwroot/css/home.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
{
1111
font-size: 156px;
1212
font-weight: 100;
13-
}
13+
}

LiveCode/wwwroot/js/session.js

+171-57
Original file line numberDiff line numberDiff line change
@@ -11,78 +11,192 @@ editor.setHighlightSelectedWord(true);
1111
editor.setDisplayIndentGuides(true);
1212
editor.renderer.setShowPrintMargin(false);
1313

14+
var pointerMapping = {};
15+
var editorScrollTop = 0;
16+
var editorScrollLeft = 0;
17+
1418
var isSilent = false;
1519

16-
connection.on("ReceiveContent", function (content, row, pos) {
17-
isSilent = true;
18-
editor.getSession().setValue(content);
19-
editor.selection.moveCursorTo(row, pos);
20-
isSilent = false;
20+
function startWithDelay(code, ms, conditionTrigger)
21+
{
22+
var timer = null;
23+
24+
var run = function ()
25+
{
26+
var args = arguments;
27+
var pThis = this;
28+
29+
if (timer)
30+
{
31+
clearTimeout(timer);
32+
}
33+
34+
timer = setTimeout(
35+
function ()
36+
{
37+
if (conditionTrigger && !conditionTrigger())
38+
{
39+
run.apply(pThis, args);
40+
return;
41+
}
42+
43+
code.apply(pThis, args);
44+
},
45+
ms);
46+
}
47+
48+
return run;
49+
}
50+
51+
function computeHash(str)
52+
{
53+
var hash = 0;
54+
for (var i = 0; i < str.length; i++)
55+
{
56+
var char = str.charCodeAt(i);
57+
hash += char;
58+
}
59+
return hash;
60+
}
61+
62+
connection.on("ReceiveContent", function (content, row, pos)
63+
{
64+
isSilent = true;
65+
editor.getSession().setValue(content);
66+
editor.selection.moveCursorTo(row, pos);
67+
isSilent = false;
2168
});
2269

23-
connection.on("ReceiveCursor", function (row, pos) {
24-
isSilent = true;
25-
editor.selection.moveCursorTo(row, pos);
26-
isSilent = false;
70+
connection.on("ReceiveCursor", function (row, pos)
71+
{
72+
isSilent = true;
73+
editor.selection.moveCursorTo(row, pos);
74+
isSilent = false;
2775
});
2876

29-
connection.on("ReceiveSelection", function (srow, spos, erow, epos) {
30-
isSilent = true;
31-
editor.selection.setSelectionAnchor(srow, spos);
32-
editor.selection.moveCursorTo(erow, epos);
33-
isSilent = false;
77+
connection.on("ReceiveSelection", function (srow, spos, erow, epos)
78+
{
79+
isSilent = true;
80+
editor.selection.setSelectionAnchor(srow, spos);
81+
editor.selection.moveCursorTo(erow, epos);
82+
isSilent = false;
3483
});
3584

36-
connection.start().then(function ()
85+
connection.on("Connected", function (connectionId)
3786
{
38-
connection.invoke("Join", SESSION_ID).catch(function (err) {
39-
return console.error(err.toString());
40-
});
41-
42-
editor.getSession().on('change', function () {
43-
44-
if (isSilent) {
45-
return;
46-
}
47-
48-
var cursor = editor.selection.getCursor();
49-
50-
connection.invoke("UpdateContent", editor.getSession().getValue(), cursor.row, cursor.column).catch(function (err) {
51-
return console.error(err.toString());
52-
});
53-
54-
});
55-
56-
editor.selection.on('changeCursor', function () {
87+
isSilent = true;
5788

58-
if (isSilent) {
59-
return;
60-
}
6189

62-
var cursor = editor.selection.getCursor();
63-
64-
connection.invoke("UpdateCursor", cursor.row, cursor.column).catch(function (err) {
65-
return console.error(err.toString());
66-
});
67-
68-
});
69-
70-
editor.selection.on('changeSelection', function () {
71-
72-
if (isSilent) {
73-
return;
74-
}
75-
76-
var range = editor.selection.getRange();
90+
isSilent = false;
91+
});
7792

78-
connection.invoke("UpdateSelection", range.start.row, range.start.column, range.end.row, range.end.column).catch(function (err) {
79-
return console.error(err.toString());
80-
});
93+
connection.on("Disconnected", function (connectionId)
94+
{
95+
isSilent = true;
96+
pointerMapping[connectionId].style.display = 'none';
97+
isSilent = false;
98+
});
8199

82-
});
100+
connection.on("ReceivePointer", function (connectionId, x, y)
101+
{
102+
isSilent = true;
103+
104+
if (!pointerMapping[connectionId])
105+
{
106+
var pointer = pointerMapping[connectionId] = document.createElement("DIV");
107+
pointer.className = 'pointer';
108+
pointer.style.filter = 'hue-rotate(' + (computeHash(connectionId) % 360) + 'deg)';
109+
document.body.appendChild(pointer);
110+
}
111+
112+
pointerMapping[connectionId].style.left = x + 'px';
113+
pointerMapping[connectionId].style.top = y + 'px';
114+
isSilent = false;
115+
});
83116

117+
connection.start().then(function ()
118+
{
119+
connection.invoke("Join", SESSION_ID).catch(function (err)
120+
{
121+
return console.error(err.toString());
122+
});
123+
124+
editor.getSession().on('change', function ()
125+
{
126+
127+
if (isSilent)
128+
{
129+
return;
130+
}
131+
132+
var cursor = editor.selection.getCursor();
133+
134+
connection.invoke("UpdateContent", editor.getSession().getValue(), cursor.row, cursor.column).catch(function (err)
135+
{
136+
return console.error(err.toString());
137+
});
138+
139+
});
140+
141+
editor.selection.on('changeCursor', function ()
142+
{
143+
144+
if (isSilent)
145+
{
146+
return;
147+
}
148+
149+
var cursor = editor.selection.getCursor();
150+
151+
connection.invoke("UpdateCursor", cursor.row, cursor.column).catch(function (err)
152+
{
153+
return console.error(err.toString());
154+
});
155+
156+
});
157+
158+
editor.selection.on('changeSelection', function ()
159+
{
160+
161+
if (isSilent)
162+
{
163+
return;
164+
}
165+
166+
var range = editor.selection.getRange();
167+
168+
connection.invoke("UpdateSelection", range.start.row, range.start.column, range.end.row, range.end.column).catch(function (err)
169+
{
170+
return console.error(err.toString());
171+
});
172+
173+
});
174+
175+
editor.getSession().on('changeScrollTop',
176+
function (scroll)
177+
{
178+
editorScrollTop = parseInt(scroll) || 0;
179+
}
180+
);
181+
editor.getSession().on('changeScrollLeft',
182+
function (scroll)
183+
{
184+
editorScrollLeft = parseInt(scroll) || 0;
185+
}
186+
);
187+
188+
document.onmousemove = startWithDelay(function (e)
189+
{
190+
var style = document.getElementsByClassName('.ace_text-input').style;
191+
192+
connection.invoke("UpdatePointer", e.pageX + editorScrollLeft, e.pageY + editorScrollTop).catch(function (err)
193+
{
194+
return console.error(err.toString());
195+
});
196+
},
197+
10);
84198

85199
}).catch(function (err)
86200
{
87-
return console.error(err.toString());
201+
return console.error(err.toString());
88202
});

0 commit comments

Comments
 (0)