sweep ClideTappable across 15 files, remove 267 lines
23 StatefulWidget+State hover pairs converted to StatelessWidget using ClideTappable(builder: (ctx, hovered, _) => ...). Covers app.dart (5), welcome (2), claude session host (2), column hat (2), spine (1), pane chrome (1), tab bar (1), decisions (1), tickets (1), graph (1), pql panel (1), backlinks (1), file tree (2), git panel (1), git status (1). Only EditorDragHandle and ClidePalette excluded (drag/keyboard behavior beyond hover+tap). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -336,7 +336,7 @@ class _FileGroup extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _GitFileRow extends StatefulWidget {
|
||||
class _GitFileRow extends StatelessWidget {
|
||||
const _GitFileRow({
|
||||
required this.entry,
|
||||
this.onStage,
|
||||
@@ -349,78 +349,65 @@ class _GitFileRow extends StatefulWidget {
|
||||
final void Function(String path)? onUnstage;
|
||||
final void Function(String path)? onDiscard;
|
||||
|
||||
@override
|
||||
State<_GitFileRow> createState() => _GitFileRowState();
|
||||
}
|
||||
|
||||
class _GitFileRowState extends State<_GitFileRow> {
|
||||
bool _hover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
final path = widget.entry['path'] as String? ?? '';
|
||||
final path = entry['path'] as String? ?? '';
|
||||
final name = path.split('/').last;
|
||||
final indexState = widget.entry['indexState'] as String?;
|
||||
final workTreeState = widget.entry['workTreeState'] as String?;
|
||||
final indexState = entry['indexState'] as String?;
|
||||
final workTreeState = entry['workTreeState'] as String?;
|
||||
final state = indexState ?? workTreeState ?? '';
|
||||
final stateLabel = _stateLabel(state);
|
||||
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) => setState(() => _hover = true),
|
||||
onExit: (_) => setState(() => _hover = false),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
return Semantics(
|
||||
button: true,
|
||||
label: '$name $stateLabel',
|
||||
child: ClideTappable(
|
||||
onTap: () {
|
||||
final kernel = ClideKernel.of(context);
|
||||
unawaited(kernel.ipc.request('editor.open', args: {'path': path}));
|
||||
},
|
||||
child: Semantics(
|
||||
button: true,
|
||||
label: '$name $stateLabel',
|
||||
child: Container(
|
||||
color: _hover ? tokens.sidebarItemHover : null,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20, right: 8, top: 2, bottom: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
ClideText(
|
||||
_stateIndicator(state),
|
||||
fontSize: clideFontCaption,
|
||||
color: _stateColor(state, tokens),
|
||||
builder: (context, hovered, _) => Container(
|
||||
color: hovered ? tokens.sidebarItemHover : null,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20, right: 8, top: 2, bottom: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
ClideText(
|
||||
_stateIndicator(state),
|
||||
fontSize: clideFontCaption,
|
||||
color: _stateColor(state, tokens),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: tokens.sidebarForeground,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: tokens.sidebarForeground,
|
||||
),
|
||||
if (hovered) ...[
|
||||
if (onStage != null)
|
||||
_SmallAction(
|
||||
label: '+',
|
||||
semanticsLabel: 'stage $name',
|
||||
onTap: () => onStage!(path),
|
||||
),
|
||||
if (onUnstage != null)
|
||||
_SmallAction(
|
||||
label: '-',
|
||||
semanticsLabel: 'unstage $name',
|
||||
onTap: () => onUnstage!(path),
|
||||
),
|
||||
if (onDiscard != null)
|
||||
_SmallAction(
|
||||
label: 'x',
|
||||
semanticsLabel: 'discard changes to $name',
|
||||
onTap: () => onDiscard!(path),
|
||||
),
|
||||
),
|
||||
if (_hover) ...[
|
||||
if (widget.onStage != null)
|
||||
_SmallAction(
|
||||
label: '+',
|
||||
semanticsLabel: 'stage $name',
|
||||
onTap: () => widget.onStage!(path),
|
||||
),
|
||||
if (widget.onUnstage != null)
|
||||
_SmallAction(
|
||||
label: '-',
|
||||
semanticsLabel: 'unstage $name',
|
||||
onTap: () => widget.onUnstage!(path),
|
||||
),
|
||||
if (widget.onDiscard != null)
|
||||
_SmallAction(
|
||||
label: 'x',
|
||||
semanticsLabel: 'discard changes to $name',
|
||||
onTap: () => widget.onDiscard!(path),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -228,7 +228,7 @@ class _BranchPickerState extends State<_BranchPicker> {
|
||||
}
|
||||
}
|
||||
|
||||
class _BranchRow extends StatefulWidget {
|
||||
class _BranchRow extends StatelessWidget {
|
||||
const _BranchRow({
|
||||
required this.name,
|
||||
required this.current,
|
||||
@@ -239,51 +239,39 @@ class _BranchRow extends StatefulWidget {
|
||||
final bool current;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
State<_BranchRow> createState() => _BranchRowState();
|
||||
}
|
||||
|
||||
class _BranchRowState extends State<_BranchRow> {
|
||||
bool _hover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
return MouseRegion(
|
||||
cursor:
|
||||
widget.onTap != null ? SystemMouseCursors.click : MouseCursor.defer,
|
||||
onEnter: (_) => setState(() => _hover = true),
|
||||
onExit: (_) => setState(() => _hover = false),
|
||||
child: GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: Container(
|
||||
color: _hover ? tokens.listItemHoverBackground : null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.current)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ClideIcon(
|
||||
const CheckIcon(),
|
||||
size: 12,
|
||||
color: tokens.statusSuccess,
|
||||
),
|
||||
)
|
||||
else
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
widget.name,
|
||||
fontFamily: clideMonoFamily,
|
||||
fontSize: clideFontMono,
|
||||
color: widget.current
|
||||
? tokens.globalForeground
|
||||
: tokens.listItemForeground,
|
||||
return ClideTappable(
|
||||
onTap: onTap,
|
||||
cursor: onTap != null ? SystemMouseCursors.click : MouseCursor.defer,
|
||||
builder: (context, hovered, _) => Container(
|
||||
color: hovered ? tokens.listItemHoverBackground : null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
if (current)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ClideIcon(
|
||||
const CheckIcon(),
|
||||
size: 12,
|
||||
color: tokens.statusSuccess,
|
||||
),
|
||||
)
|
||||
else
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
name,
|
||||
fontFamily: clideMonoFamily,
|
||||
fontSize: clideFontMono,
|
||||
color: current
|
||||
? tokens.globalForeground
|
||||
: tokens.listItemForeground,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user