extend ClideTappable with press/longpress, retrofit button + rail

ClideTappable builder now receives (context, hovered, pressed).
Optional onLongPress and onPressChanged callbacks. ClideButton
is now a StatelessWidget wrapping ClideTappable. ClideIconRail
refactored — _RailButton uses ClideTappable with tooltip.
ClideTooltip positions smart (above when near screen bottom).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 11:21:05 +02:00
co-authored by Claude Opus 4.6
parent c9870bb044
commit fbd5f1ae04
5 changed files with 129 additions and 139 deletions
+27 -14
View File
@@ -2,8 +2,6 @@ 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,
@@ -30,22 +28,37 @@ class _ClideTooltipState extends State<ClideTooltip> {
_entry?.remove();
final box = context.findRenderObject() as RenderBox?;
if (box == null) return;
final offset = box.localToGlobal(Offset(0, box.size.height + 4));
final target = box.localToGlobal(Offset.zero);
final size = box.size;
final screenSize = MediaQuery.of(context).size;
_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),
final tooltip = 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, fontSize: 12),
);
final spaceBelow = screenSize.height - target.dy - size.height;
final showAbove = spaceBelow < 60;
if (showAbove) {
return Positioned(
left: target.dx,
bottom: screenSize.height - target.dy + 4,
child: tooltip,
);
}
return Positioned(
left: target.dx,
top: target.dy + size.height + 4,
child: tooltip,
);
},
);