Skip to content

Commit

Permalink
ContextMenu documentation and member renames
Browse files Browse the repository at this point in the history
  • Loading branch information
chesnoksatan committed Jun 19, 2022
1 parent f72601c commit 575dc90
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 34 deletions.
85 changes: 62 additions & 23 deletions lib/widgets/context_menu.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import 'package:flutter/material.dart';

/// [ContextMenu] provides popup menu for the [child] widget and contains [entries].
class ContextMenu extends StatelessWidget {
/// [child] is the widget for which the context menu will be called.
final Widget child;

/// The [entries] are displayed in the order they are provided.
/// They can be [ContextMenuEntry], [ContextMenuDivider], [ContextSubMenuEntry] or any other widgets inherited from PopupMenuEntry.
final List<PopupMenuEntry> entries;

/// Whether the context menu will be displayed when tapped on a secondary button.
/// This defaults to true.
final bool openOnSecondary;

/// Whether the context menu will be displayed when a long press gesture with a primary button has been recognized.
/// This defaults to true.
final bool openOnLong;

const ContextMenu({
Expand Down Expand Up @@ -31,17 +42,28 @@ class ContextMenu extends StatelessWidget {
}
}

/// [ContextSubMenuEntry] is a [PopupMenuEntry] that displays a submenu with [entries].
class ContextSubMenuEntry extends PopupMenuEntry<String> {
/// Using for [represents] method.
final String id;
final Widget text;

/// A widget to display before the title.
/// Typically a [Icon] widget.
final Widget? leading;

/// The primary content of the menu entry.
/// Typically a [Text] widget.
final Widget title;

/// The [entries] are displayed in the order they are provided.
/// They can be [ContextMenuEntry], [ContextMenuDivider], [ContextSubMenuEntry] or any other widgets inherited from PopupMenuEntry.
final List<PopupMenuEntry> entries;
final Widget? icon;

const ContextSubMenuEntry({
required this.id,
required this.text,
this.leading,
required this.title,
required this.entries,
this.icon,
Key? key,
}) : super(key: key);

Expand All @@ -55,19 +77,16 @@ class ContextSubMenuEntry extends PopupMenuEntry<String> {
bool represents(String? value) => id == value;
}

// TODO: if not enough space, show from left
class _ContextSubMenuEntryState extends State<ContextSubMenuEntry> {
final _subMenuKey = GlobalKey();
@override
Widget build(BuildContext context) {
return InkWell(
key: _subMenuKey,
onTap: () {
final RenderBox renderBox =
_subMenuKey.currentContext?.findRenderObject() as RenderBox;
final Size size = renderBox.size;
// Get current render box of the context widget (ContextSubMenuEntry).
final RenderBox renderBox = context.findRenderObject() as RenderBox;
// Get the position where the submenu should be opened.
final Offset offset =
renderBox.localToGlobal(Offset(size.width + 1, -8));
renderBox.localToGlobal(Offset(renderBox.size.width + 1, -8));

_openContextMenu(context, offset, widget.entries);
},
Expand All @@ -76,14 +95,14 @@ class _ContextSubMenuEntryState extends State<ContextSubMenuEntry> {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
if (widget.icon != null) ...[
if (widget.leading != null) ...[
IconTheme.merge(
data: IconThemeData(
size: 20,
color:
Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
),
child: widget.icon!,
child: widget.leading!,
),
const SizedBox(width: 16),
],
Expand All @@ -95,7 +114,7 @@ class _ContextSubMenuEntryState extends State<ContextSubMenuEntry> {
Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
),
overflow: TextOverflow.ellipsis,
child: widget.text,
child: widget.title,
),
),
IconTheme.merge(
Expand All @@ -112,17 +131,30 @@ class _ContextSubMenuEntryState extends State<ContextSubMenuEntry> {
}
}

/// [ContextSubMenuEntry] is a [PopupMenuEntry] that displays a base menu entry.
class ContextMenuEntry extends PopupMenuEntry<String> {
/// Using for [represents] method.
final String id;
final Widget? icon;
final Widget text;

/// A widget to display before the title.
/// Typically a [Icon] widget.
final Widget? leading;

/// The primary content of the menu entry.
/// Typically a [Text] widget.
final Widget title;

/// A tap with a primary button has occurred.
final VoidCallback onTap;

/// Optional content to display keysequence after the title.
/// Typically a [Text] widget.
final Widget? shortcut;

const ContextMenuEntry({
required this.id,
this.icon,
required this.text,
this.leading,
required this.title,
required this.onTap,
this.shortcut,
Key? key,
Expand Down Expand Up @@ -151,14 +183,14 @@ class _ContextMenuEntryState extends State<ContextMenuEntry> {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
if (widget.icon != null) ...[
if (widget.leading != null) ...[
IconTheme.merge(
data: IconThemeData(
size: 20,
color:
Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
),
child: widget.icon!,
child: widget.leading!,
),
const SizedBox(width: 16),
],
Expand All @@ -170,7 +202,7 @@ class _ContextMenuEntryState extends State<ContextMenuEntry> {
Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
),
overflow: TextOverflow.ellipsis,
child: widget.text,
child: widget.title,
),
),
if (widget.shortcut != null)
Expand All @@ -191,12 +223,19 @@ class _ContextMenuEntryState extends State<ContextMenuEntry> {
}

/// Just for rename
/// A horizontal divider in a Material Design popup menu.
///
/// TODO: It is necessary to discuss whether such a decision is correct.
class ContextMenuDivider extends PopupMenuDivider {
const ContextMenuDivider({Key? key}) : super(key: key);
}

void _openContextMenu(BuildContext context, Offset position,
List<PopupMenuEntry<dynamic>> entries) {
/// Show a popup menu that contains the [entries] at [position].
void _openContextMenu(
BuildContext context,
Offset position,
List<PopupMenuEntry<dynamic>> entries,
) {
showMenu(
context: context,
position: RelativeRect.fromLTRB(
Expand Down
6 changes: 3 additions & 3 deletions lib/widgets/side_pane.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ class _SidePaneState extends State<SidePane> {
entries: [
ContextMenuEntry(
id: 'open',
text: const Text("Open"),
title: const Text("Open"),
onTap: () => widget.workspace.currentDir =
widget.destinations[index].path,
),
ContextMenuEntry(
id: 'open_in_new_tab',
text: const Text("Open in new tab"),
title: const Text("Open in new tab"),
onTap: () => widget.onNewTab(widget.destinations[index].path),
),
ContextMenuEntry(
id: 'open_in_new_window',
text: const Text("Open in new window"),
title: const Text("Open in new window"),
onTap: () {},
),
],
Expand Down
16 changes: 8 additions & 8 deletions lib/widgets/table.dart
Original file line number Diff line number Diff line change
Expand Up @@ -415,34 +415,34 @@ class _FilesRowState extends State<_FilesRow> {
entries: [
ContextMenuEntry(
id: 'open',
text: const Text("Open"),
title: const Text("Open"),
onTap: () {},
shortcut: const Text("Return"),
),
ContextMenuEntry(
id: 'open_with',
text: const Text("Open with other application"),
title: const Text("Open with other application"),
onTap: () {},
),
const ContextMenuDivider(),
ContextMenuEntry(
id: 'copy',
icon: const Icon(Icons.file_copy_outlined),
text: const Text("Copy file"),
leading: const Icon(Icons.file_copy_outlined),
title: const Text("Copy file"),
onTap: () {},
shortcut: const Text("Ctrl+C"),
),
ContextMenuEntry(
id: 'cut',
icon: const Icon(Icons.cut_outlined),
text: const Text("Cut file"),
leading: const Icon(Icons.cut_outlined),
title: const Text("Cut file"),
onTap: () {},
shortcut: const Text("Ctrl+X"),
),
ContextMenuEntry(
id: 'paste',
icon: const Icon(Icons.paste_outlined),
text: const Text("Paste file"),
leading: const Icon(Icons.paste_outlined),
title: const Text("Paste file"),
shortcut: const Text("Ctrl+V"),
onTap: () {},
),
Expand Down

0 comments on commit 575dc90

Please sign in to comment.