-
Notifications
You must be signed in to change notification settings - Fork 745
Milestone
Description
This is a sub-issue / prerequisite for fixing:
Type Hierarchy (Before)
IInputBinding (interface)
├── Commands: Command[]
├── Data: object?
└── Source: View? ← Added in Phase 1
KeyBinding : IInputBinding (record struct)
├── Commands, Data, Source
├── Key: Key?
└── Target: View? ← Used for app-level hotkeys
MouseBinding : IInputBinding (record struct)
├── Commands, Data, Source
└── MouseEvent: Mouse? ← Renamed from MouseEventArgs in Phase 1
InputBinding : IInputBinding (record struct) ← Added in Phase 2
├── Commands, Data, Source
ICommandContext (interface)
├── Command: Command
├── Source: View?
└── Binding: IInputBinding? ← Added in Phase 2
CommandContext : ICommandContext (record struct) ← Non-generic in Phase 3
├── Command, Source
└── Binding: IInputBinding?
Problem 1: Generic Variance Blocks Pattern Matching
// This fails - generics are invariant
if (args.Context is CommandContext<IInputBinding> ctx) // FALSE for MouseBinding!Solution: Non-generic CommandContext with IInputBinding Binding property.
Problem 2: Can't Access Binding Polymorphically
// ICommandContext doesn't have Binding
if (args.Context is ICommandContext ctx)
{
var binding = ctx.Binding; // ERROR: no such property
}Solution: Add IInputBinding Binding { get; } to ICommandContext.
Problem 3: Inconsistent Source Tracking
| Binding Type | Where Source Is Tracked |
|---|---|
KeyBinding |
Target property |
MouseBinding |
MouseEventArgs.View (nested in Mouse) |
| Programmatic | ICommandContext.Source (no binding) |
Solution: Add Source to IInputBinding interface. Keep KeyBinding.Target for backward compatibility (it serves a specific purpose for app-level hotkeys).
Problem 4: Programmatic Invocations Have No Binding
view.InvokeCommand(Command.Accept); // No binding, Source comes from contextSolution: InputBinding type for programmatic invocations. All invocations have a binding.
Proposed Type System
IInputBinding (Updated)
public interface IInputBinding
{
/// <summary>The commands this binding will invoke.</summary>
Command[] Commands { get; set; }
/// <summary>Arbitrary context data.</summary>
object? Data { get; set; }
/// <summary>
/// The View that is the origin of this binding.
/// For key bindings, this is where the binding was added.
/// For mouse bindings, this is the view that received the mouse event.
/// For programmatic invocations, this is the view that called InvokeCommand.
/// </summary>
View? Source { get; set; }
}InputBinding (New)
/// <summary>
/// A generic input binding used for programmatic command invocations
/// or when a specific binding type is not needed.
/// </summary>
public record struct InputBinding : IInputBinding
{
public InputBinding(Command[] commands, View? source = null, object? data = null)
{
Commands = commands;
Source = source;
Data = data;
}
public Command[] Commands { get; set; }
public object? Data { get; set; }
public View? Source { get; set; }
}KeyBinding (Updated)
public record struct KeyBinding : IInputBinding
{
public Command[] Commands { get; set; }
public object? Data { get; set; }
public View? Source { get; set; } // NEW: from interface
public Key? Key { get; set; }
public View? Target { get; set; } // KEEP: app-level hotkey target
}MouseBinding (Updated)
public record struct MouseBinding : IInputBinding
{
public Command[] Commands { get; set; }
public object? Data { get; set; }
public View? Source { get; set; } // NEW: from interface (replaces MouseEvent.View usage)
public Mouse? MouseEvent { get; set; } // RENAMED from MouseEventArgs
}ICommandContext (Updated)
public interface ICommandContext
{
/// <summary>The command being invoked.</summary>
Command Command { get; }
/// <summary>
/// The View that first invoked this command.
/// This remains constant during command propagation.
/// </summary>
View? Source { get; set; }
/// <summary>
/// The binding that triggered the command.
/// Use pattern matching to access specific binding types.
/// </summary>
IInputBinding Binding { get; }
}CommandContext (Non-Generic)
public record struct CommandContext : ICommandContext
{
public CommandContext(Command command, View? source, IInputBinding binding)
{
Command = command;
Source = source;
Binding = binding;
}
public Command Command { get; init; }
public View? Source { get; set; }
public IInputBinding Binding { get; init; }
}Simplified Usage
Before:
if (args.Context is CommandContext<MouseBinding> { } && checkbox.CheckedState == CheckState.Checked)
{
// If user clicks with mouse and item is already checked, do nothing
args.Handled = true;
return;
}
if (args.Context is CommandContext<KeyBinding> binding && binding.Command == Command.HotKey && checkbox.CheckedState == CheckState.Checked)After:
if (args.Context?.Binding is MouseBinding && checkbox.CheckedState == CheckState.Checked)
{
// If user clicks with mouse and item is already checked, do nothing
args.Handled = true;
return;
}
if (args.Context?.Binding is KeyBinding && args.Context.Command == Command.HotKey && checkbox.CheckedState == CheckState.Checked)Metadata
Metadata
Assignees
Labels
No labels
Type
Projects
Status
No status