Files
clide/lib/builtin/shared/reader_chrome.dart
T
jpmschweitzerandClaude Opus 4.8 5b3fbb994f move the pin toggle to the pane leading slot
The pin/unpin toggle is a mode control, not navigation — grouping it
with back/forward/jump implied they work alike. Pull it out of
ReaderActionBar into a standalone ReaderPinButton placed before the
title (ClidePaneChrome.leading), leaving the right-hand navigator to
back/forward/jump-to-pin/edit. Applies to all three readers. (T-198)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:26:23 +02:00

174 lines
5.2 KiB
Dart

/// Shared action-bar chrome for sidebar reader widgets (T-189, T-190, T-191).
///
/// Provides [ReaderActionBar] — the visible action bar: back, forward, pin,
/// edit pencil. Plugs into a [ClidePaneChrome] via its `trailing:` slot. The
/// retained back/forward history that drives it is the kernel `ReaderNav`
/// (one per right-pane reader); this file is just the buttons.
library;
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/widgets.dart';
// ---------------------------------------------------------------------------
// Action bar widget
// ---------------------------------------------------------------------------
/// The reader's **navigator** — back, forward, jump-to-pin (only while
/// pinned), and the edit pencil. Plugs into [ClidePaneChrome.trailing].
/// The pin/unpin toggle is deliberately NOT here: it's a mode control,
/// not navigation, so it lives in the pane's leading slot as a
/// standalone [ReaderPinButton] (T-198). All callbacks are optional —
/// pass null to hide/disable the corresponding button.
class ReaderActionBar extends StatelessWidget {
const ReaderActionBar({
super.key,
required this.canGoBack,
required this.canGoForward,
required this.hasPinned,
required this.onBack,
required this.onForward,
required this.onJumpToPin,
required this.onEdit,
});
final bool canGoBack;
final bool canGoForward;
final bool hasPinned;
final VoidCallback? onBack;
final VoidCallback? onForward;
/// Called when the jump-to-pin button (in the navigator) is tapped.
final VoidCallback? onJumpToPin;
/// Called when the edit pencil is tapped. Pass null to hide the button.
final VoidCallback? onEdit;
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
_ActionButton(
painter: PhosphorIcons.caretLeft,
tooltip: 'Back',
enabled: canGoBack,
onTap: canGoBack ? onBack : null,
tokens: tokens,
),
const SizedBox(width: 2),
_ActionButton(
painter: PhosphorIcons.caretRight,
tooltip: 'Forward',
enabled: canGoForward,
onTap: canGoForward ? onForward : null,
tokens: tokens,
),
if (hasPinned) ...[
const SizedBox(width: 2),
_ActionButton(
painter: PhosphorIcons.arrowUUpLeft,
tooltip: 'Jump to pin',
enabled: true,
onTap: onJumpToPin,
tokens: tokens,
),
],
if (onEdit != null) ...[
const SizedBox(width: 4),
_ActionButton(
painter: PhosphorIcons.pencilSimple,
tooltip: 'Edit in editor',
enabled: true,
onTap: onEdit,
tokens: tokens,
),
],
],
);
}
}
/// The pin/unpin toggle — a single button that sits in the pane's
/// leading slot (before the title), separate from the navigator so the
/// two kinds of control don't read as one group (T-198). [pinned]
/// drives the accent + the Pin/Unpin label; [onTap] toggles.
class ReaderPinButton extends StatelessWidget {
const ReaderPinButton({super.key, required this.pinned, required this.onTap});
final bool pinned;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return _ActionButton(
painter: PhosphorIcons.pushPin,
tooltip: pinned ? 'Unpin' : 'Pin',
enabled: onTap != null,
active: pinned,
onTap: onTap,
tokens: ClideTheme.of(context).surface,
);
}
}
// ---------------------------------------------------------------------------
// Private button widget
// ---------------------------------------------------------------------------
class _ActionButton extends StatelessWidget {
const _ActionButton({
required this.painter,
required this.tooltip,
required this.enabled,
required this.onTap,
required this.tokens,
this.active = false,
});
final ClideIconPainter painter;
final String tooltip;
final bool enabled;
/// Renders the glyph in the focus/accent colour to signal an on state
/// (used by the pin toggle when something is pinned).
final bool active;
final VoidCallback? onTap;
final SurfaceTokens tokens;
@override
Widget build(BuildContext context) {
final Color color;
if (!enabled) {
color = tokens.globalTextMuted;
} else if (active) {
color = tokens.globalFocus;
} else {
color = tokens.panelHeaderForeground;
}
return Semantics(
button: true,
label: tooltip,
enabled: enabled,
toggled: active,
onTap: onTap,
excludeSemantics: true,
child: ClideTappable(
onTap: onTap,
tooltip: tooltip,
builder: (ctx, hovered, _) => Container(
width: 20,
height: 20,
alignment: Alignment.center,
decoration: BoxDecoration(
color: hovered && enabled ? tokens.sidebarItemHover : null,
borderRadius: BorderRadius.circular(3),
),
child: ClideIcon(painter, size: 11, color: color),
),
),
);
}
}