Skip to content

Commit 43a5be4

Browse files
Feat/device tooltips (#231)
implement tooltip functionality for ViewDevice and its subclasses with layer-based filtering - Centralized tooltip logic in ViewDevice, including setup, show, and hide methods. - Added layer-based filtering for tooltip content: - ViewHost: Displays IP for Layer.Network or below, and both IP and MAC for upper layers. - ViewSwitch: Displays MAC for Layer.Network or below, and MAC with switching table for upper layers. - Tooltip is dynamically positioned below the ID label. - Subclasses only need to implement getTooltipDetails for custom tooltip content. ![image](https://github.com/user-attachments/assets/1da5220d-d944-482b-bffb-fcac2e961afd) --------- Co-authored-by: Tomás Grüner <[email protected]>
1 parent 9eb4c74 commit 43a5be4

File tree

5 files changed

+98
-0
lines changed

5 files changed

+98
-0
lines changed

src/packets/ethernet.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export class MacAddress {
4848
.join(":");
4949
}
5050

51+
toCompressedString(): string {
52+
return Array.from(this.octets)
53+
.map((byte) => (byte === 0 ? "" : byte.toString(16).padStart(2, "0")))
54+
.join(":");
55+
}
56+
5157
// Check if two MAC addresses are equal.
5258
equals(other: MacAddress): boolean {
5359
return this.octets.every((octet, index) => octet === other.octets[index]);

src/types/view-devices/vDevice.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export function layerFromType(type: DeviceType) {
5454

5555
export abstract class ViewDevice extends Container {
5656
private sprite: Sprite;
57+
private tooltip: Text | null = null; // Tooltip como un Text de PIXI.js
5758

5859
readonly id: DeviceId;
5960
readonly viewgraph: ViewGraph;
@@ -123,6 +124,9 @@ export abstract class ViewDevice extends Container {
123124
// Add device ID label using the helper function
124125
this.addDeviceIdLabel();
125126

127+
// Set up tooltip behavior
128+
this.setupHoverTooltip();
129+
126130
this.on("pointerdown", this.onPointerDown, this);
127131
this.on("click", this.onClick, this);
128132
// NOTE: this is "click" for mobile devices
@@ -135,6 +139,68 @@ export abstract class ViewDevice extends Container {
135139
// Do nothing
136140
}
137141

142+
// Tooltip setup
143+
private setupHoverTooltip() {
144+
this.on("mouseover", () => {
145+
const currentLayer = this.ctx.getCurrentLayer(); // Get the current layer from the context
146+
const tooltipMessage = this.getTooltipDetails(currentLayer); // Generate the tooltip message
147+
this.showTooltip(tooltipMessage); // Show the tooltip
148+
});
149+
150+
this.on("mouseout", () => {
151+
this.hideTooltip(); // Hide the tooltip
152+
});
153+
}
154+
155+
/**
156+
* Abstract method to get tooltip details based on the layer.
157+
* Must be implemented by derived classes.
158+
*/
159+
abstract getTooltipDetails(layer: Layer): string;
160+
161+
/**
162+
* Displays a tooltip with the provided message.
163+
*/
164+
protected showTooltip(message: string) {
165+
if (!this.tooltip) {
166+
const textStyle = new TextStyle({
167+
fontSize: 12,
168+
fill: Colors.Black,
169+
align: "center",
170+
fontWeight: "bold",
171+
});
172+
173+
this.tooltip = new Text({ text: message, style: textStyle });
174+
this.tooltip.anchor.set(0.5); // Center the text
175+
const idTextY = this.height * 0.8; // Position of the ID label
176+
this.tooltip.y = idTextY + 20; // Tooltip is 20px below the ID label
177+
this.addChild(this.tooltip); // Add the tooltip as a child
178+
}
179+
180+
this.tooltip.text = message; // Update the tooltip message
181+
this.tooltip.visible = true; // Show the tooltip
182+
}
183+
184+
/**
185+
* Hides the tooltip.
186+
*/
187+
protected hideTooltip() {
188+
if (this.tooltip) {
189+
this.tooltip.visible = false; // Hide the tooltip
190+
}
191+
}
192+
193+
/**
194+
* Removes the tooltip from the device.
195+
*/
196+
protected removeTooltip() {
197+
if (this.tooltip) {
198+
this.removeChild(this.tooltip); // Remove the tooltip from the sprite
199+
this.tooltip.destroy(); // Free memory
200+
this.tooltip = null;
201+
}
202+
}
203+
138204
updateVisibility() {
139205
this.visible = layerIncluded(this.getLayer(), this.viewgraph.getLayer());
140206
}
@@ -267,6 +333,7 @@ export abstract class ViewDevice extends Container {
267333

268334
destroy() {
269335
deselectElement();
336+
this.removeTooltip(); // Remove the tooltip if it exists
270337
super.destroy();
271338
}
272339
}

src/types/view-devices/vHost.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ export class ViewHost extends ViewNetworkDevice {
180180
this.runningPrograms.clear();
181181
}
182182

183+
getTooltipDetails(layer: Layer): string {
184+
if (layer >= Layer.Network) {
185+
// If we are in the network layer or below, show only the IP
186+
return `IP: ${this.ip.octets.join(".")}`;
187+
} else {
188+
// If we are in the upper layer, show both IP and MAC
189+
return `IP: ${this.ip.octets.join(".")}\nMAC: ${this.mac.toCompressedString()}`;
190+
}
191+
}
192+
183193
// TCP
184194

185195
private tcpModule = new TcpModule(this);

src/types/view-devices/vRouter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ export class ViewRouter extends ViewNetworkDevice {
120120
return DeviceType.Router;
121121
}
122122

123+
getTooltipDetails(layer: Layer): string {
124+
if (layer >= Layer.Network) {
125+
// If we are in the network layer or below, show only the IP
126+
return `IP: ${this.ip.octets.join(".")}`;
127+
} else {
128+
// If we are in the upper layer, show both IP and MAC
129+
return `IP: ${this.ip.octets.join(".")}\nMAC: ${this.mac.toCompressedString()}`;
130+
}
131+
}
132+
123133
setMaxQueueSize(newSize: number) {
124134
this.packetQueue.setMaxQueueSize(newSize);
125135
this.packetQueueSize = newSize;

src/types/view-devices/vSwitch.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export class ViewSwitch extends ViewDevice {
5656
return DeviceType.Switch;
5757
}
5858

59+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
60+
getTooltipDetails(_layer: Layer): string {
61+
return `MAC: ${this.mac.toCompressedString()}`;
62+
}
63+
5964
updateSwitchingTable(mac: MacAddress, deviceId: DeviceId): void {
6065
console.debug(`Adding ${mac.toString()} to the switching table`);
6166
this.viewgraph.getDataGraph().modifyDevice(this.id, (device) => {

0 commit comments

Comments
 (0)