Skip to content

Commit ddf482a

Browse files
committed
implement click-to-copy in vm_table_headers
1 parent e43933c commit ddf482a

File tree

4 files changed

+68
-54
lines changed

4 files changed

+68
-54
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import 'package:flutter/material.dart' hide Tooltip;
2+
import 'package:flutter/services.dart';
3+
import 'tooltip.dart';
4+
5+
class CopyableText extends StatefulWidget {
6+
final String text;
7+
final TextStyle? style;
8+
9+
const CopyableText(this.text, {super.key, this.style});
10+
11+
@override
12+
State<CopyableText> createState() => _CopyableTextState();
13+
}
14+
15+
class _CopyableTextState extends State<CopyableText> {
16+
bool _copied = false;
17+
18+
void _copyToClipboard() async {
19+
await Clipboard.setData(ClipboardData(text: widget.text));
20+
setState(() => _copied = true);
21+
}
22+
23+
void _resetCopied() {
24+
if (_copied) {
25+
setState(() => _copied = false);
26+
}
27+
}
28+
29+
@override
30+
Widget build(BuildContext context) {
31+
return MouseRegion(
32+
cursor: SystemMouseCursors.click,
33+
onExit: (_) => _resetCopied(),
34+
child: GestureDetector(
35+
onTap: _copyToClipboard,
36+
child: Tooltip(
37+
message: _copied ? 'Copied' : 'Click to copy',
38+
child: Text(
39+
widget.text,
40+
style: widget.style,
41+
maxLines: 1,
42+
overflow: TextOverflow.ellipsis,
43+
),
44+
),
45+
),
46+
);
47+
}
48+
}

src/client/gui/lib/vm_details/ip_addresses.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import 'package:flutter/material.dart' hide Tooltip;
2+
import '../copyable_text.dart';
23

34
import '../extensions.dart';
45
import '../tooltip.dart';
56

67
class IpAddresses extends StatelessWidget {
78
final Iterable<String> ips;
9+
final bool copyable;
810

9-
const IpAddresses(this.ips, {super.key});
11+
const IpAddresses(this.ips, {this.copyable = false, super.key});
1012

1113
@override
1214
Widget build(BuildContext context) {
@@ -15,10 +17,12 @@ class IpAddresses extends StatelessWidget {
1517

1618
return Row(children: [
1719
Expanded(
18-
child: Tooltip(
19-
message: firstIp,
20-
child: Text(firstIp.nonBreaking, overflow: TextOverflow.ellipsis),
21-
),
20+
child: copyable
21+
? CopyableText(firstIp)
22+
: Tooltip(
23+
message: firstIp,
24+
child: Text(firstIp.nonBreaking, overflow: TextOverflow.ellipsis),
25+
),
2226
),
2327
if (restIps.isNotEmpty)
2428
Badge.count(

src/client/gui/lib/vm_details/vm_details_general.dart

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,51 +12,7 @@ import 'vm_action_buttons.dart';
1212
import 'vm_details.dart';
1313
import 'vm_status_icon.dart';
1414
import '../tooltip.dart';
15-
16-
class CopyableText extends StatefulWidget {
17-
final String text;
18-
final TextStyle? style;
19-
20-
const CopyableText(this.text, {super.key, this.style});
21-
22-
@override
23-
State<CopyableText> createState() => _CopyableTextState();
24-
}
25-
26-
class _CopyableTextState extends State<CopyableText> {
27-
bool _copied = false;
28-
29-
void _copyToClipboard() async {
30-
await Clipboard.setData(ClipboardData(text: widget.text));
31-
setState(() => _copied = true);
32-
}
33-
34-
void _resetCopied() {
35-
if (_copied) {
36-
setState(() => _copied = false);
37-
}
38-
}
39-
40-
@override
41-
Widget build(BuildContext context) {
42-
return MouseRegion(
43-
cursor: SystemMouseCursors.click,
44-
onExit: (_) => _resetCopied(),
45-
child: GestureDetector(
46-
onTap: _copyToClipboard,
47-
child: Tooltip(
48-
message: _copied ? 'Copied' : 'Click to copy',
49-
child: Text(
50-
widget.text,
51-
style: widget.style,
52-
maxLines: 1,
53-
overflow: TextOverflow.ellipsis,
54-
),
55-
),
56-
),
57-
);
58-
}
59-
}
15+
import '../copyable_text.dart';
6016

6117
class VmDetailsHeader extends ConsumerWidget {
6218
final String name;

src/client/gui/lib/vm_table/vm_table_headers.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import '../vm_details/vm_status_icon.dart';
1414
import 'search_box.dart';
1515
import 'table.dart';
1616
import 'vms.dart';
17+
import '../copyable_text.dart';
1718

1819
final headers = <TableHeader<VmInfo>>[
1920
TableHeader(
@@ -72,23 +73,28 @@ final headers = <TableHeader<VmInfo>>[
7273
minWidth: 70,
7374
cellBuilder: (info) {
7475
final image = info.instanceInfo.currentRelease;
75-
return Text(
76+
return CopyableText(
7677
image.isNotBlank ? image.nonBreaking : '-',
77-
overflow: TextOverflow.ellipsis,
7878
);
7979
},
8080
),
8181
TableHeader(
8282
name: 'PRIVATE IP',
8383
width: 140,
8484
minWidth: 100,
85-
cellBuilder: (info) => IpAddresses(info.instanceInfo.ipv4.take(1)),
85+
cellBuilder: (info) => IpAddresses(
86+
info.instanceInfo.ipv4.take(1),
87+
copyable: true,
88+
),
8689
),
8790
TableHeader(
8891
name: 'PUBLIC IP',
8992
width: 140,
9093
minWidth: 100,
91-
cellBuilder: (info) => IpAddresses(info.instanceInfo.ipv4.skip(1)),
94+
cellBuilder: (info) => IpAddresses(
95+
info.instanceInfo.ipv4.skip(1),
96+
copyable: true,
97+
),
9298
),
9399
];
94100

0 commit comments

Comments
 (0)