Files
clide/lib/widgets/src/clide_tooltip.dart
T
jpmschweitzerandClaude Opus 4.6 46329700d5 dissolve app/ into repo root (D-056)
Single Flutter package at the repo root. All code, tests, assets,
and platform directories moved from app/ to root. Package renamed
from clide_app to clide — all imports rewritten. Merged pubspec
combines core (ffi) and app (flutter, yaml, xterm) dependencies.
Makefile simplified: no APP_PRESENT conditionals, no cd, no daemon
lifecycle. 317 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 00:37:20 +02:00

85 lines
2.2 KiB
Dart

import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_text.dart';
import 'package:flutter/widgets.dart';
/// Very small tooltip — hover a child to reveal a label. Uses an
/// OverlayEntry to draw above everything else without Material.
class ClideTooltip extends StatefulWidget {
const ClideTooltip({
super.key,
required this.message,
required this.child,
this.showDelay = const Duration(milliseconds: 500),
});
final String message;
final Widget child;
final Duration showDelay;
@override
State<ClideTooltip> createState() => _ClideTooltipState();
}
class _ClideTooltipState extends State<ClideTooltip> {
OverlayEntry? _entry;
bool _hovering = false;
void _show() {
final overlay = Overlay.maybeOf(context);
if (overlay == null) return;
_entry?.remove();
final box = context.findRenderObject() as RenderBox?;
if (box == null) return;
final offset = box.localToGlobal(Offset(0, box.size.height + 4));
_entry = OverlayEntry(
builder: (ctx) {
final tokens = ClideTheme.of(ctx).surface;
return Positioned(
left: offset.dx,
top: offset.dy,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: tokens.tooltipBackground,
border: Border.all(color: tokens.tooltipBorder),
borderRadius: BorderRadius.circular(3),
),
child: ClideText(widget.message, color: tokens.tooltipForeground),
),
);
},
);
overlay.insert(_entry!);
}
void _hide() {
_entry?.remove();
_entry = null;
}
@override
void dispose() {
_hide();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Semantics(
tooltip: widget.message,
child: MouseRegion(
onEnter: (_) async {
_hovering = true;
await Future<void>.delayed(widget.showDelay);
if (mounted && _hovering) _show();
},
onExit: (_) {
_hovering = false;
_hide();
},
child: widget.child,
),
);
}
}