mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 16:59:35 +01:00
Add comprehensive CWP pattern analysis and recommendations
Co-authored-by: tig <585482+tig@users.noreply.github.com>
This commit is contained in:
457
docs/analysis/cwp_analysis_report.md
Normal file
457
docs/analysis/cwp_analysis_report.md
Normal file
@@ -0,0 +1,457 @@
|
||||
# Cancellable Work Pattern (CWP) Analysis Report
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This report analyzes all instances of the Cancellable Work Pattern (CWP) in the Terminal.Gui codebase to assess the potential impact of reversing the calling order from:
|
||||
- **Current**: Virtual method first → Event second
|
||||
- **Proposed**: Event first → Virtual method second
|
||||
|
||||
## Background
|
||||
|
||||
The CWP pattern currently calls the virtual method (`OnXxx`) before invoking the event (`Xxx`). This gives inherited code (via override) priority over external code (via event subscription). Issue #3714 raises concerns about this order, particularly for mouse events where external code cannot prevent events from reaching overridden methods in views.
|
||||
|
||||
## Analysis Methodology
|
||||
|
||||
1. Identified all `protected virtual bool OnXxx` methods in Terminal.Gui
|
||||
2. Located their corresponding event invocations
|
||||
3. Analyzed the calling context and dependencies
|
||||
4. Assessed impact of order reversal
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### 1. Mouse Events
|
||||
|
||||
#### 1.1 OnMouseEvent / MouseEvent
|
||||
**File**: `Terminal.Gui/ViewBase/View.Mouse.cs:348`
|
||||
**Calling Context**:
|
||||
```csharp
|
||||
public bool RaiseMouseEvent (MouseEventArgs mouseEvent)
|
||||
{
|
||||
if (OnMouseEvent (mouseEvent) || mouseEvent.Handled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
MouseEvent?.Invoke (this, mouseEvent);
|
||||
return mouseEvent.Handled;
|
||||
}
|
||||
```
|
||||
|
||||
**Current Order**: `OnMouseEvent` → `MouseEvent`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **HIGH IMPACT**: This is the primary mouse event handler
|
||||
- **Problem**: Views like `Slider` that override `OnMouseEvent` cannot be prevented from handling mouse events by external code
|
||||
- **Dependencies**: Many views override `OnMouseEvent` expecting priority
|
||||
- **Reversing Order Would**:
|
||||
- Allow external code to cancel before view's override processes
|
||||
- Break existing assumptions in views that expect first access
|
||||
- Require review of all `OnMouseEvent` overrides
|
||||
|
||||
#### 1.2 OnMouseClick / MouseClick
|
||||
**File**: `Terminal.Gui/ViewBase/View.Mouse.cs:515`
|
||||
**Calling Context**:
|
||||
```csharp
|
||||
protected bool RaiseMouseClickEvent (MouseEventArgs args)
|
||||
{
|
||||
if (OnMouseClick (args) || args.Handled)
|
||||
{
|
||||
return args.Handled;
|
||||
}
|
||||
MouseClick?.Invoke (this, args);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Current Order**: `OnMouseClick` → `MouseClick`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **HIGH IMPACT**: Same issue as `OnMouseEvent`
|
||||
- **Problem**: External code cannot prevent clicks from reaching view's override
|
||||
- **Reversing Order Would**:
|
||||
- Enable external mouse click prevention
|
||||
- Consistent with `OnMouseEvent` changes
|
||||
- Requires coordination with all click handlers
|
||||
|
||||
#### 1.3 OnMouseEnter / MouseEnter
|
||||
**File**: `Terminal.Gui/ViewBase/View.Mouse.cs:125`
|
||||
**Calling Context**:
|
||||
```csharp
|
||||
internal bool? NewMouseEnterEvent (CancelEventArgs eventArgs)
|
||||
{
|
||||
if (OnMouseEnter (eventArgs))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
MouseEnter?.Invoke (this, eventArgs);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Current Order**: `OnMouseEnter` → `MouseEnter`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM IMPACT**: Less commonly overridden
|
||||
- **Reversing Order Would**:
|
||||
- Allow external code to prevent enter notifications
|
||||
- Minimal breaking changes expected
|
||||
|
||||
#### 1.4 OnMouseWheel / MouseWheel
|
||||
**File**: `Terminal.Gui/ViewBase/View.Mouse.cs:610`
|
||||
**Calling Context**:
|
||||
```csharp
|
||||
protected bool RaiseMouseWheelEvent (MouseEventArgs args)
|
||||
{
|
||||
if (OnMouseWheel (args) || args.Handled)
|
||||
{
|
||||
return args.Handled;
|
||||
}
|
||||
MouseWheel?.Invoke (this, args);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Current Order**: `OnMouseWheel` → `MouseWheel`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM IMPACT**: Wheel handling is specific
|
||||
- **Reversing Order Would**:
|
||||
- Enable external scroll prevention
|
||||
- Useful for modal dialogs or locked views
|
||||
|
||||
### 2. Keyboard Events
|
||||
|
||||
#### 2.1 OnKeyDown / KeyDown
|
||||
**File**: `Terminal.Gui/ViewBase/View.Keyboard.cs:367`
|
||||
**Calling Context**:
|
||||
```csharp
|
||||
public bool NewKeyDownEvent (Key k)
|
||||
{
|
||||
if (OnKeyDown (k) || k.Handled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
KeyDown?.Invoke (this, k);
|
||||
return k.Handled;
|
||||
}
|
||||
```
|
||||
|
||||
**Current Order**: `OnKeyDown` → `KeyDown`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **HIGH IMPACT**: Core keyboard input handling
|
||||
- **Dependencies**: Many views override for input processing
|
||||
- **Reversing Order Would**:
|
||||
- Allow external key interception before view processing
|
||||
- Could break views expecting first access to keys
|
||||
- Major behavioral change for input handling
|
||||
|
||||
#### 2.2 OnKeyDownNotHandled / KeyDownNotHandled
|
||||
**File**: `Terminal.Gui/ViewBase/View.Keyboard.cs:399`
|
||||
**Current Order**: `OnKeyDownNotHandled` → `KeyDownNotHandled`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **LOW IMPACT**: Secondary handler for unhandled keys
|
||||
- **Reversing Order Would**:
|
||||
- Minor change, already handles unhandled keys
|
||||
- Low risk
|
||||
|
||||
#### 2.3 OnKeyUp / KeyUp
|
||||
**File**: `Terminal.Gui/ViewBase/View.Keyboard.cs` (implementation in NewKeyUpEvent)
|
||||
**Current Order**: `OnKeyUp` → `KeyUp`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: Less commonly used than KeyDown
|
||||
- **Reversing Order Would**:
|
||||
- Enable external key-up prevention
|
||||
- Lower risk than KeyDown
|
||||
|
||||
### 3. Command Events
|
||||
|
||||
#### 3.1 OnAccepting / Accepting
|
||||
**File**: `Terminal.Gui/ViewBase/View.Command.cs:190`
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM-HIGH IMPACT**: Core command pattern
|
||||
- **Used by**: CheckBox, RadioGroup, Button, etc.
|
||||
- **Reversing Order Would**:
|
||||
- Allow external cancellation before view logic
|
||||
- May break views expecting to set state first
|
||||
|
||||
#### 3.2 OnSelecting / Selecting
|
||||
**File**: `Terminal.Gui/ViewBase/View.Command.cs:245`
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM IMPACT**: Selection behavior
|
||||
- **Reversing Order Would**:
|
||||
- Enable external selection control
|
||||
- Useful for validation scenarios
|
||||
|
||||
#### 3.3 OnHandlingHotKey / HandlingHotKey
|
||||
**File**: `Terminal.Gui/ViewBase/View.Command.cs:291`
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: HotKey processing
|
||||
- **Reversing Order Would**:
|
||||
- Allow hotkey override by external code
|
||||
- Could be useful for dynamic hotkey management
|
||||
|
||||
#### 3.4 OnCommandNotBound / CommandNotBound
|
||||
**File**: `Terminal.Gui/ViewBase/View.Command.cs:93`
|
||||
**Impact Analysis**:
|
||||
- **LOW IMPACT**: Fallback for unmapped commands
|
||||
- **Reversing Order Would**:
|
||||
- Minor impact, handles unbound commands
|
||||
|
||||
### 4. Drawing Events
|
||||
|
||||
#### 4.1 Drawing Pipeline Events
|
||||
**Files**: `Terminal.Gui/ViewBase/View.Drawing.cs`
|
||||
- `OnDrawingAdornments` / `DrawingAdornments`
|
||||
- `OnClearingViewport` / `ClearingViewport`
|
||||
- `OnDrawingText` / `DrawingText`
|
||||
- `OnDrawingContent` / `DrawingContent`
|
||||
- `OnDrawingSubViews` / `DrawingSubViews`
|
||||
- `OnRenderingLineCanvas` / `RenderingLineCanvas`
|
||||
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: Rendering customization
|
||||
- **Reversing Order Would**:
|
||||
- Allow external drawing interception
|
||||
- Useful for debugging/tracing
|
||||
- Lower risk as drawing is less state-dependent
|
||||
|
||||
### 5. Navigation Events
|
||||
|
||||
#### 5.1 OnAdvancingFocus / AdvancingFocus
|
||||
**File**: `Terminal.Gui/ViewBase/View.Navigation.cs:208`
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM-HIGH IMPACT**: Focus management
|
||||
- **Reversing Order Would**:
|
||||
- Allow external focus control
|
||||
- Could break focus flow expectations
|
||||
|
||||
#### 5.2 OnHasFocusChanging / HasFocusChanging
|
||||
**File**: `Terminal.Gui/ViewBase/View.Navigation.cs:717`
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM-HIGH IMPACT**: Focus state management
|
||||
- **Reversing Order Would**:
|
||||
- Enable external focus validation
|
||||
- Useful for preventing focus changes
|
||||
|
||||
### 6. Property Change Events
|
||||
|
||||
#### 6.1 OnSchemeNameChanging / SchemeNameChanging
|
||||
**File**: `Terminal.Gui/ViewBase/View.Drawing.Scheme.cs:51`
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: Scheme change validation
|
||||
- **Uses**: `CWPPropertyHelper`
|
||||
- **Reversing Order Would**:
|
||||
- Allow external scheme validation
|
||||
- Low risk
|
||||
|
||||
#### 6.2 OnGettingScheme / GettingScheme
|
||||
**File**: `Terminal.Gui/ViewBase/View.Drawing.Scheme.cs:170`
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: Scheme resolution
|
||||
- **Reversing Order Would**:
|
||||
- Allow external scheme override
|
||||
- Useful for theming
|
||||
|
||||
#### 6.3 OnSettingScheme / SettingScheme
|
||||
**File**: `Terminal.Gui/ViewBase/View.Drawing.Scheme.cs:241`
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: Scheme application
|
||||
- **Reversing Order Would**:
|
||||
- Enable external scheme interception
|
||||
- Low risk
|
||||
|
||||
#### 6.4 OnVisibleChanging / VisibleChanging
|
||||
**File**: `Terminal.Gui/ViewBase/View.cs:382`
|
||||
**Impact Analysis**:
|
||||
- **MEDIUM IMPACT**: Visibility state management
|
||||
- **Reversing Order Would**:
|
||||
- Allow external visibility control
|
||||
- Could affect layout calculations
|
||||
|
||||
#### 6.5 OnOrientationChanging (commented out)
|
||||
**File**: `Terminal.Gui/ViewBase/Orientation/OrientationHelper.cs:166`
|
||||
**Impact Analysis**:
|
||||
- **N/A**: Currently commented out
|
||||
- **Note**: OrientationHelper uses callback pattern
|
||||
|
||||
### 7. View-Specific Events
|
||||
|
||||
#### 7.1 OnCellActivated (TableView)
|
||||
**File**: `Terminal.Gui/Views/TableView/TableView.cs:1277`
|
||||
**Impact Analysis**:
|
||||
- **LOW IMPACT**: TableView-specific
|
||||
- **Reversing Order Would**:
|
||||
- Allow external cell activation control
|
||||
- Isolated to TableView
|
||||
|
||||
#### 7.2 OnPositionChanging (ScrollBar, ScrollSlider)
|
||||
**Files**:
|
||||
- `Terminal.Gui/Views/ScrollBar/ScrollBar.cs:375`
|
||||
- `Terminal.Gui/Views/ScrollBar/ScrollSlider.cs:245`
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: Scrollbar-specific
|
||||
- **Reversing Order Would**:
|
||||
- Enable external scroll validation
|
||||
- Useful for scroll constraints
|
||||
|
||||
#### 7.3 OnCheckedStateChanging (CheckBox)
|
||||
**File**: `Terminal.Gui/Views/CheckBox.cs:187`
|
||||
**Impact Analysis**:
|
||||
- **LOW-MEDIUM IMPACT**: CheckBox state management
|
||||
- **Reversing Order Would**:
|
||||
- Allow external state validation
|
||||
- Useful for validation logic
|
||||
|
||||
### 8. Other Events
|
||||
|
||||
#### 8.1 OnBorderStyleChanged
|
||||
**File**: `Terminal.Gui/ViewBase/View.Adornments.cs:162`
|
||||
**Impact Analysis**:
|
||||
- **LOW IMPACT**: Border styling
|
||||
- **Reversing Order Would**:
|
||||
- Enable external border control
|
||||
- Minimal risk
|
||||
|
||||
#### 8.2 OnMouseIsHeldDownTick (MouseHeldDown)
|
||||
**File**: `Terminal.Gui/ViewBase/MouseHeldDown.cs:71`
|
||||
**Impact Analysis**:
|
||||
- **LOW IMPACT**: Continuous mouse press
|
||||
- **Reversing Order Would**:
|
||||
- Allow external hold-down control
|
||||
- Minimal risk
|
||||
|
||||
#### 8.3 OnGettingAttributeForRole
|
||||
**File**: `Terminal.Gui/ViewBase/View.Drawing.Attribute.cs:78`
|
||||
**Impact Analysis**:
|
||||
- **LOW IMPACT**: Attribute resolution
|
||||
- **Reversing Order Would**:
|
||||
- Enable external attribute override
|
||||
- Useful for accessibility
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
**Total CWP Implementations Found**: 33
|
||||
|
||||
**By Impact Level**:
|
||||
- **HIGH IMPACT**: 3 (Mouse/Keyboard core events)
|
||||
- **MEDIUM-HIGH IMPACT**: 4 (Commands, Navigation)
|
||||
- **MEDIUM IMPACT**: 8 (Various UI events)
|
||||
- **LOW-MEDIUM IMPACT**: 10 (Property changes, view-specific)
|
||||
- **LOW IMPACT**: 8 (Specialized/rare events)
|
||||
|
||||
## Code Dependencies on Current Order
|
||||
|
||||
### Tests That Validate Order
|
||||
Located in `Tests/UnitTestsParallelizable/View/Orientation/OrientationTests.cs`:
|
||||
```csharp
|
||||
[Fact]
|
||||
public void OrientationChanging_VirtualMethodCalledBeforeEvent()
|
||||
{
|
||||
// Test explicitly validates virtual method is called before event
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OrientationChanged_VirtualMethodCalledBeforeEvent()
|
||||
{
|
||||
// Test explicitly validates virtual method is called before event
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: These tests will FAIL if order is reversed - they explicitly test current behavior.
|
||||
|
||||
### Views With Override Dependencies
|
||||
Based on code search, many views override these methods and may depend on being called first:
|
||||
- `Slider.OnMouseEvent` - Core issue from #3714
|
||||
- Various views override `OnKeyDown` for input handling
|
||||
- Command handlers in Button, CheckBox, RadioGroup
|
||||
- Custom drawing in various views
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Option 1: Reverse Order Globally
|
||||
**Pros**:
|
||||
- Solves #3714 completely
|
||||
- Consistent pattern across all events
|
||||
- External code gets priority (looser coupling)
|
||||
|
||||
**Cons**:
|
||||
- MAJOR BREAKING CHANGE
|
||||
- Requires updating all views that override CWP methods
|
||||
- Extensive testing required
|
||||
- May break user code
|
||||
|
||||
**Effort**: HIGH (4-6 weeks)
|
||||
**Risk**: HIGH
|
||||
|
||||
### Option 2: Add "Before" Events
|
||||
**Pros**:
|
||||
- Non-breaking
|
||||
- Explicit control for when needed
|
||||
- Gradual migration path
|
||||
|
||||
**Cons**:
|
||||
- More API surface
|
||||
- Complexity in naming/documentation
|
||||
- Two patterns coexist
|
||||
|
||||
**Effort**: MEDIUM (2-3 weeks)
|
||||
**Risk**: LOW
|
||||
|
||||
### Option 3: Add `IgnoreMouse` Property
|
||||
**Pros**:
|
||||
- Minimal change
|
||||
- Solves immediate #3714 issue
|
||||
- No breaking changes
|
||||
|
||||
**Cons**:
|
||||
- Band-aid solution
|
||||
- Doesn't address root cause
|
||||
- Only solves mouse issue
|
||||
|
||||
**Effort**: LOW (1 week)
|
||||
**Risk**: VERY LOW
|
||||
|
||||
### Option 4: Reverse Order for Mouse Events Only
|
||||
**Pros**:
|
||||
- Solves #3714
|
||||
- Limited scope reduces risk
|
||||
- Mouse is primary concern
|
||||
|
||||
**Cons**:
|
||||
- Inconsistent pattern
|
||||
- Still breaking for mouse overrides
|
||||
- Confusion with different orders
|
||||
|
||||
**Effort**: MEDIUM (2 weeks)
|
||||
**Risk**: MEDIUM
|
||||
|
||||
## Conclusion
|
||||
|
||||
The analysis reveals that reversing CWP order globally would be a **significant breaking change** affecting 33+ event pairs across the codebase. The **highest impact** would be on:
|
||||
|
||||
1. **Mouse events** (OnMouseEvent, OnMouseClick) - direct issue #3714
|
||||
2. **Keyboard events** (OnKeyDown) - core input handling
|
||||
3. **Command events** (OnAccepting, OnSelecting) - state management
|
||||
4. **Navigation events** (OnAdvancingFocus, OnHasFocusChanging) - focus flow
|
||||
|
||||
**Recommended Approach**:
|
||||
- **Short-term**: Option 3 (IgnoreMouse property) or Option 2 (Before events for mouse only)
|
||||
- **Long-term**: Consider Option 2 with gradual migration, or accept current design as intentional
|
||||
|
||||
The current design prioritizes **inheritance over composition**, which may be appropriate for a UI framework where tight coupling through inheritance is common. However, it limits external control, which is the root cause of #3714.
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Review this analysis with maintainers
|
||||
2. Decide on approach based on project priorities
|
||||
3. If proceeding with order reversal:
|
||||
- Create comprehensive test plan
|
||||
- Update all affected views
|
||||
- Update documentation
|
||||
- Provide migration guide
|
||||
4. If proceeding with alternative:
|
||||
- Implement chosen solution
|
||||
- Update documentation with rationale
|
||||
- Add examples for working around current limitation
|
||||
Reference in New Issue
Block a user