handleMouseEvent method
Handles a mouse event, performing hit testing and routing to layers.
Implementation
void handleMouseEvent(MouseEvent event) {
final meta = <String, String>{
'button': event.button.name,
'type': event.type.name,
'pos': '${event.x},${event.y}',
};
if (event.modifiers.isNotEmpty) {
meta['modifiers'] = event.modifiers.map((m) => m.name).join('+');
}
Tracer.record(
_traceMouseEventId,
Phase.begin,
TraceCategory.events,
metadata: meta,
);
try {
_globalMouseX = event.x - 1;
_globalMouseY = event.y - 1;
_isGlobalMouseDown =
event.type == MouseEventType.press ||
event.type == MouseEventType.drag;
if (debugShowTouchesEnabled) {
if (event.type == MouseEventType.press ||
event.type == MouseEventType.release) {
_activeTouches.add(
_TouchFeedback(
x: _globalMouseX!,
y: _globalMouseY!,
button: event.button,
type: event.type,
startTime: clock.now().millisecondsSinceEpoch,
),
);
addRipple(Point<int>(event.x, event.y));
}
}
var didRender = false;
if (event.type == MouseEventType.press) {
final mouseX = event.x - 1;
final mouseY = event.y - 1;
final sortedLayers = _getSortedLayers();
SceneLayer? hitLayer;
for (final layer in sortedLayers) {
if (!layer.hitTestable) continue;
final buf = layer.renderer.currentBuffer;
if (buf == null) continue;
if (mouseX >= layer.x &&
mouseX < layer.x + buf.width &&
mouseY >= layer.y &&
mouseY < layer.y + buf.height) {
final isTL = mouseX == layer.x && mouseY == layer.y;
final isTR = mouseX == layer.x + buf.width - 1 && mouseY == layer.y;
final isBL =
mouseX == layer.x && mouseY == layer.y + buf.height - 1;
final isBR =
mouseX == layer.x + buf.width - 1 &&
mouseY == layer.y + buf.height - 1;
if (layer.resizable && (isTL || isTR || isBL || isBR)) {
hitLayer = layer;
_resizingLayer = layer;
_resizeCorner = isTL
? 1
: isTR
? 2
: isBL
? 3
: 4;
_dragStartX = event.x;
_dragStartY = event.y;
_layerStartX = layer.x;
_layerStartY = layer.y;
_layerStartWidth = layer.width ?? buf.width;
_layerStartHeight = layer.height ?? buf.height;
break;
}
final localX = mouseX - layer.x;
final localY = mouseY - layer.y;
final localEvent = MouseEvent(
x: localX + 1,
y: localY + 1,
globalX: event.globalX ?? event.x,
globalY: event.globalY ?? event.y,
button: event.button,
type: event.type,
modifiers: event.modifiers,
);
final isTransparent =
(buf.getModifiers(localX, localY) & Modifier.transparent) != 0;
final isOpaque = !isTransparent;
final handled = layer.renderer.handleMouseEvent(localEvent);
if (handled || layer.draggable) {
hitLayer = layer;
if (layer.draggable) {
_draggingLayer = layer;
_dragStartX = event.x;
_dragStartY = event.y;
_layerStartX = layer.x;
_layerStartY = layer.y;
}
break;
}
if (isOpaque) {
if (layer.mouseOpaque) {
hitLayer = layer;
}
break;
}
}
}
if (hitLayer != null) {
_capturedMouseLayer = hitLayer;
if (focusedLayer != hitLayer) {
focusedLayer = hitLayer;
hitLayer.onFocus?.call();
scheduleRender();
}
}
} else if (event.type == MouseEventType.drag) {
if (_resizingLayer != null) {
int dx = event.x - _dragStartX;
int dy = event.y - _dragStartY;
if (_resizeCorner == 1 || _resizeCorner == 3) {
if (_layerStartWidth - dx < 10) dx = _layerStartWidth - 10;
} else {
if (_layerStartWidth + dx < 10) dx = 10 - _layerStartWidth;
}
if (_resizeCorner == 1 || _resizeCorner == 2) {
if (_layerStartHeight - dy < 5) dy = _layerStartHeight - 5;
} else {
if (_layerStartHeight + dy < 5) dy = 5 - _layerStartHeight;
}
var newX = _layerStartX;
var newY = _layerStartY;
var newW = _layerStartWidth;
var newH = _layerStartHeight;
if (_resizeCorner == 1) {
newX += dx;
newY += dy;
newW -= dx;
newH -= dy;
} else if (_resizeCorner == 2) {
newY += dy;
newW += dx;
newH -= dy;
} else if (_resizeCorner == 3) {
newX += dx;
newW -= dx;
newH += dy;
} else if (_resizeCorner == 4) {
newW += dx;
newH += dy;
}
_resizingLayer!.x = newX;
_resizingLayer!.y = newY;
_resizingLayer!.width = newW;
_resizingLayer!.height = newH;
_resizeRenderer(_resizingLayer!.renderer, newW, newH);
render();
didRender = true;
} else if (_draggingLayer != null) {
final dx = event.x - _dragStartX;
final dy = event.y - _dragStartY;
_draggingLayer!.x = _layerStartX + dx;
_draggingLayer!.y = _layerStartY + dy;
render();
didRender = true;
} else if (_capturedMouseLayer != null) {
final mouseX = event.x - 1;
final mouseY = event.y - 1;
final localX = mouseX - _capturedMouseLayer!.x;
final localY = mouseY - _capturedMouseLayer!.y;
final localEvent = MouseEvent(
x: localX + 1,
y: localY + 1,
globalX: event.globalX ?? event.x,
globalY: event.globalY ?? event.y,
button: event.button,
type: event.type,
modifiers: event.modifiers,
);
_capturedMouseLayer!.renderer.handleMouseEvent(localEvent);
}
} else if (event.type == MouseEventType.release) {
if (_resizingLayer != null) {
_resizingLayer = null;
_resizeCorner = 0;
_capturedMouseLayer = null;
} else {
final targetLayer = _draggingLayer ?? _capturedMouseLayer;
if (targetLayer != null) {
final mouseX = event.x - 1;
final mouseY = event.y - 1;
final localX = mouseX - targetLayer.x;
final localY = mouseY - targetLayer.y;
final localEvent = MouseEvent(
x: localX + 1,
y: localY + 1,
globalX: event.globalX ?? event.x,
globalY: event.globalY ?? event.y,
button: event.button,
type: event.type,
modifiers: event.modifiers,
);
targetLayer.renderer.handleMouseEvent(localEvent);
}
_draggingLayer = null;
_capturedMouseLayer = null;
}
} else {
// For hover (move) events, perform hit-test and route
final mouseX = event.x - 1;
final mouseY = event.y - 1;
final sortedLayers = _getSortedLayers();
SceneLayer? hitLayer;
for (final layer in sortedLayers) {
if (!layer.hitTestable) continue;
final buf = layer.renderer.currentBuffer;
if (buf == null) continue;
if (mouseX >= layer.x &&
mouseX < layer.x + buf.width &&
mouseY >= layer.y &&
mouseY < layer.y + buf.height) {
final localX = mouseX - layer.x;
final localY = mouseY - layer.y;
final localEvent = MouseEvent(
x: localX + 1,
y: localY + 1,
globalX: event.globalX ?? event.x,
globalY: event.globalY ?? event.y,
button: event.button,
type: event.type,
modifiers: event.modifiers,
);
final isTransparent =
(buf.getModifiers(localX, localY) & Modifier.transparent) != 0;
final isOpaque = !isTransparent;
final handled = layer.renderer.handleMouseEvent(localEvent);
if (handled) {
hitLayer = layer;
break;
}
if (isOpaque) {
if (layer.mouseOpaque) {
hitLayer = layer;
}
break;
}
}
}
if (_lastHoveredLayer != null && _lastHoveredLayer != hitLayer) {
final localEvent = MouseEvent(
x: -1,
y: -1,
globalX: event.globalX ?? event.x,
globalY: event.globalY ?? event.y,
button: event.button,
type: event.type,
modifiers: event.modifiers,
);
_lastHoveredLayer!.renderer.handleMouseEvent(localEvent);
}
_lastHoveredLayer = hitLayer;
}
if (debugMouseCursorEnabled && !didRender) {
render();
}
} finally {
Tracer.record(_traceMouseEventId, Phase.end, TraceCategory.events);
}
}