瀏覽代碼

Rewrite and expand Popovers.md documentation

Completely overhauled Popovers.md with a comprehensive guide to popovers in Terminal.Gui. Added detailed explanations, requirements, and best practices for creating, registering, and managing popovers. Included extensive code examples for PopoverMenu, custom popovers, and common scenarios such as context menus and autocomplete. Documented keyboard and mouse event handling, lifecycle management, and provided an API reference. Removed outdated and unstructured content.
Tig 3 天之前
父節點
當前提交
21407c710f
共有 1 個文件被更改,包括 361 次插入11 次删除
  1. 361 11
      docfx/docs/Popovers.md

+ 361 - 11
docfx/docs/Popovers.md

@@ -1,18 +1,368 @@
 # Popovers Deep Dive
 
-Normally Views cannot draw outside of their `Viewport`. Options for influencing content outside of the `Viewport` include:
+Popovers are transient UI elements that appear above other content to display contextual information, such as menus, tooltips, autocomplete suggestions, and dialog boxes. Terminal.Gui's popover system provides a flexible, non-modal way to present temporary UI without blocking the rest of the application.
 
-1) Modifying the `Border` behavior
-2) Modifying the `Margin` behavior
-3) Using @Terminal.Gui.App.Application.Popover
+## Overview
 
-Popovers are useful for scenarios such as menus, autocomplete popups, and drop-down combo boxes.
+Normally, Views cannot draw outside of their `Viewport`. To display content that appears to "pop over" other views, Terminal.Gui provides the popover system via @Terminal.Gui.App.Application.Popover. Popovers differ from alternatives like modifying `Border` or `Margin` behavior because they:
 
-A `Popover` is any View that meets these characteristics:
+- Are managed centrally by the application
+- Support focus and keyboard event routing
+- Automatically hide in response to user actions
+- Can receive global hotkeys even when not visible
 
-- Implements the @Terminal.Gui.App.IPopover interface 
-- Is Focusable (`CetFocus = true`)
-- Is Transparent (`ViewportSettings = ViewportSettings.Transparent | ViewportSettings.TransparentMouse`
-- Sets `Visible = false` when it receives `Application.QuitKey`
+## Creating a Popover
 
[email protected] provides a sophisticated implementation that can be used as a context menu and is the basis for @Terminal.Gui.MenuBar.
+### Using PopoverMenu
+
+The easiest way to create a popover is to use @Terminal.Gui.Views.PopoverMenu, which provides a cascading menu implementation:
+
+```csharp
+// Create a popover menu with menu items
+PopoverMenu contextMenu = new ([
+    new MenuItem ("Cut", Command.Cut),
+    new MenuItem ("Copy", Command.Copy),
+    new MenuItem ("Paste", Command.Paste),
+    new MenuItem ("Select All", Command.SelectAll)
+]);
+
+// IMPORTANT: Register before showing
+Application.Popover?.Register (contextMenu);
+
+// Show at mouse position or specific location
+contextMenu.MakeVisible (); // Uses current mouse position
+// OR
+contextMenu.MakeVisible (new Point (10, 5)); // Specific location
+```
+
+### Creating a Custom Popover
+
+To create a custom popover, inherit from @Terminal.Gui.App.PopoverBaseImpl:
+
+```csharp
+public class MyCustomPopover : PopoverBaseImpl
+{
+    public MyCustomPopover ()
+    {
+        // PopoverBaseImpl already sets up required defaults:
+        // - ViewportSettings with Transparent and TransparentMouse flags
+        // - Command.Quit binding to hide the popover
+        // - Width/Height set to Dim.Fill()
+        
+        // Add your custom content
+        Label label = new () { Text = "Custom Popover Content" };
+        Add (label);
+        
+        // Optionally override size
+        Width = 40;
+        Height = 10;
+    }
+}
+
+// Usage:
+MyCustomPopover myPopover = new ();
+Application.Popover?.Register (myPopover);
+Application.Popover?.Show (myPopover);
+```
+
+## Popover Requirements
+
+A View qualifies as a popover if it:
+
+1. **Implements @Terminal.Gui.App.IPopover** - Provides the `Current` property for runnable association
+2. **Is Focusable** - `CanFocus = true` to receive keyboard input
+3. **Is Transparent** - `ViewportSettings` includes both:
+   - `ViewportSettings.Transparent` - Allows content beneath to show through
+   - `ViewportSettings.TransparentMouse` - Mouse clicks outside subviews pass through
+4. **Handles Quit** - Binds `Application.QuitKey` to `Command.Quit` and sets `Visible = false`
+
[email protected] provides all these requirements by default.
+
+## Registration and Lifecycle
+
+### Registration (REQUIRED)
+
+**All popovers must be registered before they can be shown:**
+
+```csharp
+PopoverMenu popover = new ([...]);
+
+// REQUIRED: Register with the application
+Application.Popover?.Register (popover);
+
+// Now you can show it
+Application.Popover?.Show (popover);
+// OR
+popover.MakeVisible (); // For PopoverMenu
+```
+
+**Why Registration is Required:**
+- Enables keyboard event routing to the popover
+- Allows global hotkeys to work even when popover is hidden
+- Manages popover lifecycle and disposal
+
+### Showing and Hiding
+
+**Show a popover:**
+```csharp
+Application.Popover?.Show (popover);
+```
+
+**Hide a popover:**
+```csharp
+// Method 1: Via ApplicationPopover
+Application.Popover?.Hide (popover);
+
+// Method 2: Set Visible property
+popover.Visible = false;
+
+// Automatic hiding occurs when:
+// - User presses Application.QuitKey (typically Esc)
+// - User clicks outside the popover (not on a subview)
+// - Another popover is shown
+```
+
+### Lifecycle Management
+
+**Registered popovers:**
+- Have their lifetime managed by the application
+- Are automatically disposed when `Application.Shutdown ()` is called
+- Receive keyboard events based on their associated runnable
+
+**To manage lifetime manually:**
+```csharp
+// Deregister to take ownership of disposal
+Application.Popover?.DeRegister (popover);
+
+// Now you're responsible for disposal
+popover.Dispose ();
+```
+
+## Keyboard Event Routing
+
+### Global Hotkeys
+
+Registered popovers receive keyboard events even when not visible, enabling global hotkey support:
+
+```csharp
+PopoverMenu menu = new ([...]);
+menu.Key = Key.F10.WithShift; // Default hotkey
+
+Application.Popover?.Register (menu);
+
+// Now pressing Shift+F10 anywhere in the app will show the menu
+```
+
+### Runnable Association
+
+The @Terminal.Gui.App.IPopover.Current property associates a popover with a specific @Terminal.Gui.IRunnable:
+
+- If `null`: Popover receives all keyboard events from the application
+- If set: Popover only receives events when the associated runnable is active
+- Automatically set to `Application.TopRunnableView` during registration
+
+```csharp
+// Associate with a specific runnable
+myPopover.Current = myWindow; // Only active when myWindow is the top runnable
+```
+
+## Focus and Input
+
+**When visible:**
+- Popovers receive focus automatically
+- All keyboard input goes to the popover until hidden
+- Mouse clicks on subviews are captured
+- Mouse clicks outside subviews pass through (due to `TransparentMouse`)
+
+**When hidden:**
+- Only registered hotkeys are processed
+- Other keyboard input is not captured
+
+## Layout and Positioning
+
+### Default Layout
+
[email protected] sets `Width = Dim.Fill ()` and `Height = Dim.Fill ()`, making the popover fill the screen by default. The transparent viewport settings allow content beneath to remain visible.
+
+### Custom Sizing
+
+Override `Width` and `Height` to customize size:
+
+```csharp
+public class MyPopover : PopoverBaseImpl
+{
+    public MyPopover ()
+    {
+        Width = 40;  // Fixed width
+        Height = Dim.Auto (); // Auto height based on content
+    }
+}
+```
+
+### Positioning with PopoverMenu
+
[email protected] provides positioning helpers:
+
+```csharp
+// Position at specific screen coordinates
+menu.SetPosition (new Point (10, 5));
+
+// Show and position in one call
+menu.MakeVisible (new Point (10, 5));
+
+// Uses mouse position if null
+menu.MakeVisible (); // Uses Application.Mouse.LastMousePosition
+```
+
+The menu automatically adjusts position to ensure it remains fully visible on screen.
+
+## Built-in Popover Types
+
+### PopoverMenu
+
[email protected] is a sophisticated cascading menu implementation used for:
+- Context menus
+- @Terminal.Gui.MenuBar drop-down menus
+- Custom menu scenarios
+
+**Key Features:**
+- Cascading submenus with automatic positioning
+- Keyboard navigation (arrow keys, hotkeys)
+- Automatic key binding from Commands
+- Mouse support
+- Separator lines via `new Line ()`
+
+**Example with submenus:**
+```csharp
+PopoverMenu fileMenu = new ([
+    new MenuItem ("New", Command.New),
+    new MenuItem ("Open", Command.Open),
+    new MenuItem {
+        Title = "Recent",
+        SubMenu = new Menu ([
+            new MenuItem ("File1.txt", Command.Open),
+            new MenuItem ("File2.txt", Command.Open)
+        ])
+    },
+    new Line (),
+    new MenuItem ("Exit", Command.Quit)
+]);
+
+Application.Popover?.Register (fileMenu);
+fileMenu.MakeVisible ();
+```
+
+## Mouse Event Handling
+
+Popovers use `ViewportSettings.TransparentMouse`, which means:
+
+- **Clicks on popover subviews**: Captured and handled normally
+- **Clicks outside subviews**: Pass through to views beneath
+- **Clicks on background**: Automatically hide the popover
+
+This creates the expected behavior where clicking outside a menu or dialog closes it.
+
+## Best Practices
+
+1. **Always Register First**
+   ```csharp
+   // WRONG - Will throw InvalidOperationException
+   PopoverMenu menu = new ([...]);
+   menu.MakeVisible ();
+   
+   // CORRECT
+   PopoverMenu menu = new ([...]);
+   Application.Popover?.Register (menu);
+   menu.MakeVisible ();
+   ```
+
+2. **Use PopoverMenu for Menus**
+   - Don't reinvent the wheel for standard menu scenarios
+   - Leverage built-in keyboard navigation and positioning
+
+3. **Manage Lifecycle Appropriately**
+   - Let the application manage disposal for long-lived popovers
+   - Deregister and manually dispose short-lived or conditional popovers
+
+4. **Test Global Hotkeys**
+   - Ensure hotkeys don't conflict with application-level keys
+   - Consider providing configuration for custom hotkeys
+
+5. **Handle Edge Cases**
+   - Test positioning near screen edges
+   - Verify behavior with multiple runnables
+   - Test with keyboard-only navigation
+
+## Common Scenarios
+
+### Context Menu on Right-Click
+
+```csharp
+PopoverMenu contextMenu = new ([...]);
+contextMenu.MouseFlags = MouseFlags.Button3Clicked; // Right-click
+Application.Popover?.Register (contextMenu);
+
+myView.MouseClick += (s, e) =>
+{
+    if (e.MouseEvent.Flags == MouseFlags.Button3Clicked)
+    {
+        contextMenu.MakeVisible (myView.ScreenToViewport (e.MouseEvent.Position));
+        e.Handled = true;
+    }
+};
+```
+
+### Autocomplete Popup
+
+```csharp
+public class AutocompletePopover : PopoverBaseImpl
+{
+    private ListView _listView;
+    
+    public AutocompletePopover ()
+    {
+        Width = 30;
+        Height = 10;
+        
+        _listView = new ListView
+        {
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        };
+        Add (_listView);
+    }
+    
+    public void ShowSuggestions (IEnumerable<string> suggestions, Point position)
+    {
+        _listView.SetSource (suggestions.ToList ());
+        // Position below the text entry field
+        X = position.X;
+        Y = position.Y + 1;
+        Visible = true;
+    }
+}
+```
+
+### Global Command Palette
+
+```csharp
+PopoverMenu commandPalette = new (GetAllCommands ());
+commandPalette.Key = Key.P.WithCtrl; // Ctrl+P to show
+
+Application.Popover?.Register (commandPalette);
+
+// Now Ctrl+P anywhere in the app shows the command palette
+```
+
+## API Reference
+
+- @Terminal.Gui.App.IPopover - Interface for popover views
+- @Terminal.Gui.App.PopoverBaseImpl - Abstract base class for custom popovers
+- @Terminal.Gui.Views.PopoverMenu - Cascading menu implementation
+- @Terminal.Gui.App.ApplicationPopover - Popover manager (accessed via `Application.Popover`)
+
+## See Also
+
+- [Keyboard Deep Dive](keyboard.md) - Understanding keyboard event routing
+- [Mouse Deep Dive](mouse.md) - Mouse event handling
+- [MenuBar Overview](menubar.md) - Using PopoverMenu with MenuBar