Skip to content

Commit db5a70e

Browse files
authored
Feat/add tooltips (#239)
This pull request introduces a tooltip management system across multiple components and standardizes program names using constants. The most significant changes include the addition of tooltip functionality for various graphical elements, the use of centralized constants for program names, and the cleanup of redundant tooltip methods in some classes. ### Tooltip Management Enhancements: * [`src/graphics/renderables/canvas_tooltip_manager.ts`](diffhunk://#diff-12514062324b37b274095f2071082bcd2246c86351737e85bcec26d0db7850d2R1-R72): Added utility functions (`showTooltip`, `hideTooltip`, `removeTooltip`) to manage tooltips, including creation, visibility toggling, and removal. * [`src/types/edge.ts`](diffhunk://#diff-e56be54c8f80b249c61fe21bce40994324ca16c953bd58c6b8dd2d78ab64550cR11-R22): Introduced hover-based tooltips for edges, displaying interface information. Added methods like `setupHoverTooltip`, `showTooltips`, and `removeTooltips` to manage tooltip behavior. [[1]](diffhunk://#diff-e56be54c8f80b249c61fe21bce40994324ca16c953bd58c6b8dd2d78ab64550cR11-R22) [[2]](diffhunk://#diff-e56be54c8f80b249c61fe21bce40994324ca16c953bd58c6b8dd2d78ab64550cR41-L43) [[3]](diffhunk://#diff-e56be54c8f80b249c61fe21bce40994324ca16c953bd58c6b8dd2d78ab64550cR240-R321) * [`src/types/packet.ts`](diffhunk://#diff-e546d6337dcb4e908b738659b527f00387c07ab30674ab0dfbd61edab3d03ff4R93): Added hover-based tooltips for packets, displaying packet type information. Integrated tooltip management with `setupHoverTooltip`. [[1]](diffhunk://#diff-e546d6337dcb4e908b738659b527f00387c07ab30674ab0dfbd61edab3d03ff4R93) [[2]](diffhunk://#diff-e546d6337dcb4e908b738659b527f00387c07ab30674ab0dfbd61edab3d03ff4R329-R341) * [`src/types/view-devices/vDevice.ts`](diffhunk://#diff-2bb090dfab27a6fdd9154c9d52e4bb771254dcb3685cea653537c1266720bd4eL142-R163): Refactored tooltip handling for devices to use centralized tooltip utilities, replacing custom tooltip methods. [[1]](diffhunk://#diff-2bb090dfab27a6fdd9154c9d52e4bb771254dcb3685cea653537c1266720bd4eL142-R163) [[2]](diffhunk://#diff-2bb090dfab27a6fdd9154c9d52e4bb771254dcb3685cea653537c1266720bd4eL336-R305) ### Program Name Standardization: * Multiple files (`src/programs/arp_protocol.ts`, `src/programs/echo_sender.ts`, `src/programs/http_client.ts`): Replaced hardcoded program names with constants from `TOOLTIP_KEYS` for consistency. [[1]](diffhunk://#diff-73d02eab30d55d41500cd0a71aa8e7f419adec94c1c39750afd378a59d9255e7R10-R14) [[2]](diffhunk://#diff-9a573e48365a1c0d2b46d32b2e4c752bedd0c3569544861db5dc2ab0e0fd23aeR11-R14) [[3]](diffhunk://#diff-0f650b51a4265ea385ca0d0aa8c26070fb0b00b67dad5d0dcaf085aaa16f4065R9-R13) ### Constants Update: * [`src/utils/constants/tooltips_constants.ts`](diffhunk://#diff-d23a6aa67e252dd864d40b8b3b4cc29db716b0e742da79d7ef8da9b9dc956ce1L21-R24): Added new constants for program names and tooltip content, such as `SEND_ARP_REQUEST` and `SERVE_HTTP_REQUESTS`. [[1]](diffhunk://#diff-d23a6aa67e252dd864d40b8b3b4cc29db716b0e742da79d7ef8da9b9dc956ce1L21-R24) [[2]](diffhunk://#diff-d23a6aa67e252dd864d40b8b3b4cc29db716b0e742da79d7ef8da9b9dc956ce1R193-R196) These changes improve maintainability, enhance user interaction with tooltips, and ensure consistency in how program names are defined and used.
1 parent 9b0197a commit db5a70e

File tree

8 files changed

+214
-59
lines changed

8 files changed

+214
-59
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Text, TextStyle, Container } from "pixi.js";
2+
3+
/**
4+
* Displays a tooltip in a specific container.
5+
* @param container - The container where the tooltip will be displayed.
6+
* @param message - The message to display in the tooltip.
7+
* @param x - The X position of the tooltip.
8+
* @param y - The Y position of the tooltip.
9+
* @param existingTooltip - An existing tooltip (if already created).
10+
* @returns The updated or newly created tooltip.
11+
*/
12+
export function showTooltip(
13+
container: Container,
14+
message: string,
15+
x: number,
16+
y: number,
17+
existingTooltip: Text | null,
18+
): Text {
19+
if (!existingTooltip) {
20+
const textStyle = new TextStyle({
21+
fontSize: 12,
22+
fill: 0x000000, // Black
23+
align: "center",
24+
fontWeight: "bold",
25+
});
26+
27+
const tooltip = new Text({ text: message, style: textStyle });
28+
tooltip.anchor.set(0.5);
29+
container.addChild(tooltip);
30+
31+
tooltip.x = x;
32+
tooltip.y = y;
33+
tooltip.visible = true;
34+
35+
return tooltip;
36+
}
37+
38+
// If the tooltip already exists, update its position and message
39+
existingTooltip.text = message;
40+
existingTooltip.x = x;
41+
existingTooltip.y = y;
42+
existingTooltip.visible = true;
43+
44+
return existingTooltip;
45+
}
46+
47+
/**
48+
* Hides an existing tooltip.
49+
* @param tooltip - The tooltip to hide.
50+
*/
51+
export function hideTooltip(tooltip: Text | null): void {
52+
if (tooltip) {
53+
tooltip.visible = false;
54+
}
55+
}
56+
57+
/**
58+
* Removes a tooltip from the container and frees memory.
59+
* @param container - The container of the tooltip.
60+
* @param tooltip - The tooltip to remove.
61+
* @returns `null` to indicate that the tooltip has been removed.
62+
*/
63+
export function removeTooltip(
64+
container: Container,
65+
tooltip: Text | null,
66+
): null {
67+
if (tooltip) {
68+
container.removeChild(tooltip);
69+
tooltip.destroy();
70+
}
71+
return null;
72+
}

src/programs/arp_protocol.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { EthernetFrame, FramePayload, MacAddress } from "../packets/ethernet";
77
import { sendViewPacket } from "../types/packet";
88
import { IpAddress } from "../packets/ip";
99
import { ArpRequest } from "../packets/arp";
10+
import { TOOLTIP_KEYS } from "../utils/constants/tooltips_constants";
1011

1112
// ARP Request/Response
1213
export class ArpProtocol extends ProgramBase {
13-
static readonly PROGRAM_NAME = "Send ARP request";
14+
static readonly PROGRAM_NAME = TOOLTIP_KEYS.SEND_ARP_REQUEST;
1415

1516
protected dstIp: IpAddress;
1617

src/programs/echo_sender.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { EchoRequest } from "../packets/icmp";
88
import { IPv4Packet } from "../packets/ip";
99
import { ViewNetworkDevice } from "../types/view-devices/vNetworkDevice";
1010
import { EthernetFrame } from "../packets/ethernet";
11+
import { TOOLTIP_KEYS } from "../utils/constants/tooltips_constants";
1112

1213
export class SingleEcho extends ProgramBase {
13-
static readonly PROGRAM_NAME = "Send ICMP echo";
14+
static readonly PROGRAM_NAME = TOOLTIP_KEYS.SEND_ICMP_ECHO;
1415

1516
protected dstId: DeviceId;
1617

@@ -85,7 +86,7 @@ export class SingleEcho extends ProgramBase {
8586
}
8687

8788
export class EchoServer extends ProgramBase {
88-
static readonly PROGRAM_NAME = "Echo server";
89+
static readonly PROGRAM_NAME = TOOLTIP_KEYS.ECHO_SERVER;
8990

9091
private echoProgram: SingleEcho;
9192
private progress = 0;

src/programs/http_client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { Layer } from "../types/layer";
66
import { AsyncQueue } from "../types/network-modules/asyncQueue";
77
import { TcpSocket } from "../types/network-modules/tcpModule";
88
import { ViewHost } from "../types/view-devices";
9+
import { TOOLTIP_KEYS } from "../utils/constants/tooltips_constants";
910
import { ProgramBase } from "./program_base";
1011

1112
export class HttpClient extends ProgramBase {
12-
static readonly PROGRAM_NAME = "Send HTTP request";
13+
static readonly PROGRAM_NAME = TOOLTIP_KEYS.SEND_HTTP_REQUEST;
1314

1415
private dstId: DeviceId;
1516

@@ -88,7 +89,7 @@ export class HttpClient extends ProgramBase {
8889
}
8990

9091
export class HttpServer extends ProgramBase {
91-
static readonly PROGRAM_NAME = "Serve HTTP requests";
92+
static readonly PROGRAM_NAME = TOOLTIP_KEYS.SERVE_HTTP_REQUESTS;
9293

9394
private port: number;
9495

src/types/edge.ts

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Graphics, Point } from "pixi.js";
1+
import { Graphics, Point, Text } from "pixi.js";
22
import { ViewGraph } from "./graphs/viewgraph";
33
import { ViewDevice } from "./view-devices/index"; // Import the Device class
44
import { deselectElement, selectElement } from "./viewportManager";
@@ -8,11 +8,18 @@ import { Packet } from "./packet";
88
import { EdgeInfo } from "../graphics/renderables/edge_info";
99
import { DataEdge, DeviceId } from "./graphs/datagraph";
1010
import { MacAddress } from "../packets/ethernet";
11+
import {
12+
hideTooltip,
13+
removeTooltip,
14+
showTooltip,
15+
} from "../graphics/renderables/canvas_tooltip_manager";
1116

1217
export class Edge extends Graphics {
1318
private _data: DataEdge;
1419
private startPos: Point;
1520
private endPos: Point;
21+
private startTooltip: Text | null = null; // Tooltip para el extremo inicial
22+
private endTooltip: Text | null = null; // Tooltip para el extremo final
1623

1724
viewgraph: ViewGraph;
1825

@@ -31,16 +38,14 @@ export class Edge extends Graphics {
3138
this.eventMode = "static";
3239
this.interactive = true;
3340
this.cursor = "pointer";
41+
this.setupHoverTooltip();
3442
this.on("click", () => selectElement(this));
3543
// NOTE: this is "click" for mobile devices
3644
this.on("tap", () => selectElement(this));
3745

3846
this.refresh();
3947
}
4048

41-
/**
42-
* Recomputes the edge's position based on the connected devices.
43-
*/
4449
refresh() {
4550
const n1 = this.data.from.id;
4651
const n2 = this.data.to.id;
@@ -123,6 +128,7 @@ export class Edge extends Graphics {
123128

124129
destroy(): void {
125130
deselectElement();
131+
this.removeTooltips();
126132
super.destroy();
127133
}
128134

@@ -235,4 +241,86 @@ export class Edge extends Graphics {
235241
.getFreeInterfaces(deviceId);
236242
return freeIfaces;
237243
}
244+
245+
private setupHoverTooltip() {
246+
this.on("mouseover", () => {
247+
this.showTooltips();
248+
this.fixTooltipPositions(); // fixes the tooltip positions
249+
});
250+
251+
this.on("mouseout", () => {
252+
this.hideTooltips();
253+
});
254+
}
255+
256+
private showTooltips() {
257+
const startIface = this.data.from.iface;
258+
const endIface = this.data.to.iface;
259+
260+
const offsetX = 20;
261+
const offsetY = -10;
262+
263+
this.startTooltip = showTooltip(
264+
this,
265+
`eth${startIface}`,
266+
this.startPos.x + offsetX,
267+
this.startPos.y + offsetY,
268+
this.startTooltip,
269+
);
270+
271+
this.endTooltip = showTooltip(
272+
this,
273+
`eth${endIface}`,
274+
this.endPos.x - offsetX,
275+
this.endPos.y + offsetY,
276+
this.endTooltip,
277+
);
278+
}
279+
280+
private fixTooltipPositions() {
281+
const offsetX = 20; // Offset on the X-axis to move it away from the device
282+
const offsetY = -10; // Offset on the Y-axis to bring it closer to the Edge
283+
284+
// Determine if the start point is to the left or right of the end point
285+
const isStartLeft = this.startPos.x < this.endPos.x;
286+
287+
// Check the visibility of the starting device
288+
const device1 = this.viewgraph.getDevice(this.data.from.id);
289+
if (this.startTooltip) {
290+
if (device1 && device1.visible) {
291+
// If the starting device is visible, update the tooltip position
292+
this.startTooltip.x =
293+
this.startPos.x + (isStartLeft ? offsetX : -offsetX);
294+
this.startTooltip.y = this.startPos.y + offsetY;
295+
this.startTooltip.visible = true;
296+
} else {
297+
// If not visible, hide the tooltip
298+
this.startTooltip.visible = false;
299+
}
300+
}
301+
302+
// Check the visibility of the ending device
303+
const device2 = this.viewgraph.getDevice(this.data.to.id);
304+
if (this.endTooltip) {
305+
if (device2 && device2.visible) {
306+
// If the ending device is visible, update the tooltip position
307+
this.endTooltip.x = this.endPos.x + (isStartLeft ? -offsetX : offsetX);
308+
this.endTooltip.y = this.endPos.y + offsetY;
309+
this.endTooltip.visible = true;
310+
} else {
311+
// If not visible, hide the tooltip
312+
this.endTooltip.visible = false;
313+
}
314+
}
315+
}
316+
317+
private hideTooltips() {
318+
hideTooltip(this.startTooltip);
319+
hideTooltip(this.endTooltip);
320+
}
321+
322+
private removeTooltips() {
323+
this.startTooltip = removeTooltip(this, this.startTooltip);
324+
this.endTooltip = removeTooltip(this, this.endTooltip);
325+
}
238326
}

src/types/packet.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Graphics,
44
GraphicsContext,
55
Ticker,
6+
Text,
67
} from "pixi.js";
78
import { deselectElement, isSelected, selectElement } from "./viewportManager";
89
import { circleGraphicsContext, Colors, ZIndexLevels } from "../utils/utils";
@@ -29,6 +30,11 @@ import {
2930
IcmpPacket,
3031
} from "../packets/icmp";
3132
import { PacketInfo } from "../graphics/renderables/packet_info";
33+
import {
34+
hideTooltip,
35+
removeTooltip,
36+
showTooltip,
37+
} from "../graphics/renderables/canvas_tooltip_manager";
3238

3339
const contextPerPacketType: Record<string, GraphicsContext> = {
3440
HTTP: circleGraphicsContext(Colors.Hazel, 5), // for HTTP
@@ -84,6 +90,7 @@ export class Packet extends Graphics {
8490
rawPacket: EthernetFrame;
8591
ctx: GlobalContext;
8692
belongingLayer: Layer;
93+
tooltip: Text | null = null;
8794

8895
constructor(
8996
ctx: GlobalContext,
@@ -105,6 +112,7 @@ export class Packet extends Graphics {
105112
this.ctx = ctx;
106113
this.interactive = true;
107114
this.cursor = "pointer";
115+
this.setupHoverTooltip();
108116
this.on("click", this.onClick, this);
109117
this.on("tap", this.onClick, this);
110118
// register in Packet Manager
@@ -322,8 +330,19 @@ export class Packet extends Graphics {
322330

323331
// Deregister packet from PacketManager
324332
this.ctx.getViewGraph().getPacketManager().deregisterPacket(this.packetId);
333+
removeTooltip(this, this.tooltip);
325334
this.destroy();
326335
}
336+
337+
private setupHoverTooltip() {
338+
this.on("mouseover", () => {
339+
this.tooltip = showTooltip(this, this.type, 0, -15, this.tooltip);
340+
});
341+
342+
this.on("mouseout", () => {
343+
hideTooltip(this.tooltip);
344+
});
345+
}
327346
}
328347

329348
// TODO: Replace and nextHopId with the sending interface. Like this, the function

0 commit comments

Comments
 (0)