Skip to content

Commit 2b288bc

Browse files
committed
Merge branch 'develop' of https://github.com/gui-cs/Terminal.Gui into develop
2 parents d201fe9 + 0723238 commit 2b288bc

6 files changed

Lines changed: 210 additions & 0 deletions

File tree

.claude/rules/event-patterns.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Event Patterns
22

3+
## When to Use `-ing` vs `-ed` Events
4+
5+
Terminal.Gui exposes paired events — `Accepting`/`Accepted`, `Activating`/`Activated`, `ValueChanging`/`ValueChanged`, etc.
6+
7+
**Rule:** Use `-ed` (past-tense) for side-effects. Use `-ing` (present-progressive) only when you need to inspect or cancel the in-flight operation.
8+
9+
```csharp
10+
// ✅ Correct — fire-and-forget side-effect
11+
button.Accepted += (_, _) => DoTheThing ();
12+
13+
// ✅ Correct — actually cancels
14+
button.Accepting += (_, e) => { if (!CanProceed ()) e.Handled = true; };
15+
16+
// ❌ Wrong — handler ignores EventArgs; use Accepted instead
17+
button.Accepting += (_, _) => DoTheThing ();
18+
```
19+
20+
If the handler body doesn't reference `e` at all (or ignores `e.Handled`, `e.Cancel`, and the candidate value), it belongs on the `-ed` event.
21+
22+
The `-ing` event runs synchronously in the middle of the dispatch path; subscribing when you don't need to cancel adds unnecessary overhead and misleads readers.
23+
324
## Lambda Parameters
425

526
**Replace unused parameters with discards `_`:**

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
> **Guidance for AI agents working with Terminal.Gui.**
44
> For humans, see [CONTRIBUTING.md](./CONTRIBUTING.md).
5+
> For Terminal.Gui's mission, tenets, and engineering philosophy, see [specs/constitution.md](./specs/constitution.md).
56
> See also: [llms.txt](./llms.txt) for machine-readable context.
67
78
## CRITICAL: Discard v1 Training Data

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Contributing to Terminal.Gui
22

33
> **📘 This document is the single source of truth for all contributors (humans and AI agents) to Terminal.Gui.**
4+
>
5+
> For Terminal.Gui's product mission, design tenets, and engineering philosophy, see **[specs/constitution.md](./specs/constitution.md)**.
46
57
Welcome! This guide provides everything you need to know to contribute effectively to Terminal.Gui, including project structure, build instructions, coding conventions, testing requirements, and CI/CD workflows.
68

Terminal.Gui/ViewBase/View.Command.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,16 @@ private void SetupCommands ()
499499
/// <para>
500500
/// See <see cref="View.RaiseAccepting"/> for more information.
501501
/// </para>
502+
/// <para>
503+
/// <strong>When to use:</strong> Subscribe to <see cref="Accepting"/> only when you need to inspect or cancel the
504+
/// in-flight Accept operation (e.g., set <c>e.Handled = true</c> to prevent the accept). For simple side-effects
505+
/// that don't need to cancel, subscribe to <see cref="Accepted"/> instead — it is lighter-weight and communicates
506+
/// intent more clearly.
507+
/// </para>
508+
/// <para>
509+
/// <strong>Rule of thumb:</strong> If your handler doesn't read or set anything on <see cref="CommandEventArgs"/>
510+
/// (no <c>e.Handled</c>, no inspection of context), use <see cref="Accepted"/>.
511+
/// </para>
502512
/// </remarks>
503513
public event EventHandler<CommandEventArgs>? Accepting;
504514

@@ -543,6 +553,19 @@ protected virtual void OnAccepted (ICommandContext? ctx) { }
543553
/// <para>
544554
/// See <see cref="RaiseAccepted"/> for more information.
545555
/// </para>
556+
/// <para>
557+
/// <strong>When to use:</strong> Subscribe to <see cref="Accepted"/> for fire-and-forget side-effects — things that
558+
/// happen <em>after</em> the accept has completed and cannot be cancelled. This is the right choice for the vast
559+
/// majority of button-click–style handlers.
560+
/// </para>
561+
/// <para>
562+
/// <strong>Example:</strong>
563+
/// <code>
564+
/// button.Accepted += (_, _) =&gt; DoTheThing (); // correct — side-effect only
565+
/// button.Accepting += (_, e) =&gt; { if (!CanProceed ()) e.Handled = true; }; // correct — cancels
566+
/// button.Accepting += (_, _) =&gt; DoTheThing (); // wrong — use Accepted instead
567+
/// </code>
568+
/// </para>
546569
/// </remarks>
547570
public event EventHandler<CommandEventArgs>? Accepted;
548571

@@ -843,6 +866,18 @@ private void BubbleActivatedUp (ICommandContext? ctx, bool compositeOnly = false
843866
/// Set CommandEventArgs.Handled to <see langword="true"/> to indicate the event was handled and processing should
844867
/// stop.
845868
/// </summary>
869+
/// <remarks>
870+
/// <para>
871+
/// <strong>When to use:</strong> Subscribe to <see cref="Activating"/> only when you need to inspect or cancel the
872+
/// in-flight Activate operation (e.g., set <c>e.Handled = true</c> to prevent the state change). For simple
873+
/// side-effects that don't need to cancel, subscribe to <see cref="Activated"/> instead — it is lighter-weight and
874+
/// communicates intent more clearly.
875+
/// </para>
876+
/// <para>
877+
/// <strong>Rule of thumb:</strong> If your handler doesn't read or set anything on <see cref="CommandEventArgs"/>
878+
/// (no <c>e.Handled</c>, no inspection of context), use <see cref="Activated"/>.
879+
/// </para>
880+
/// </remarks>
846881
public event EventHandler<CommandEventArgs>? Activating;
847882

848883
/// <summary>
@@ -913,6 +948,16 @@ protected virtual void OnActivated (ICommandContext? ctx) { }
913948
/// Event raised when the user has performed an action (e.g. <see cref="Command.Activate"/>) causing the
914949
/// View to change state or preparing it for interaction.
915950
/// </summary>
951+
/// <remarks>
952+
/// <para>
953+
/// Unlike <see cref="Activating"/>, this event cannot be cancelled. It is raised after the View has activated.
954+
/// </para>
955+
/// <para>
956+
/// <strong>When to use:</strong> Subscribe to <see cref="Activated"/> for fire-and-forget side-effects — things
957+
/// that happen <em>after</em> the activation has completed and cannot be cancelled. This is the right choice for
958+
/// the vast majority of state-change–reaction handlers.
959+
/// </para>
960+
/// </remarks>
916961
public event EventHandler<EventArgs<ICommandContext?>>? Activated;
917962

918963
#endregion Activate

docfx/docs/events.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,37 @@ Use this decision tree to choose the right pattern:
4242
| Simple notification (no cancel) | `EventHandler` | [Recipe 3](#recipe-3-simple-notification) |
4343
| Property notification (MVVM) | `INotifyPropertyChanged` | [Recipe 4](#recipe-4-mvvm-property-notification) |
4444

45+
## When to Use `-ing` vs `-ed` Events
46+
47+
Terminal.Gui exposes paired events on many surfaces — `Accepting`/`Accepted`, `Activating`/`Activated`, `ValueChanging`/`ValueChanged`, etc. Use this rule to choose:
48+
49+
> **Use `-ed` (past-tense) events for side-effects. Use `-ing` (present-progressive) events only when you actually need to inspect or cancel the in-flight operation.**
50+
51+
If your handler doesn't read or set anything on the `EventArgs` (no `e.Handled`, no `e.Cancel`, no inspection of the candidate value), you want the `-ed` event. The `-ing` event runs synchronously in the middle of the dispatch path and is heavier for both the framework and the reader of your code.
52+
53+
### Concrete Examples
54+
55+
```csharp
56+
// ✅ Correct — fire-and-forget side-effect belongs on the -ed event
57+
button.Accepted += (_, _) => DoTheThing ();
58+
59+
// ✅ Correct — actually needs to cancel, so -ing is right
60+
button.Accepting += (_, e) => { if (!CanProceed ()) e.Handled = true; };
61+
62+
// ❌ Wrong — handler ignores EventArgs; should use Accepted
63+
button.Accepting += (_, _) => DoTheThing ();
64+
```
65+
66+
The same rule applies to every other paired event in the framework:
67+
68+
| Use `-ed` (side-effect) | Use `-ing` (inspect / cancel) |
69+
|-------------------------|-------------------------------|
70+
| `Accepted` | `Accepting` |
71+
| `Activated` | `Activating` |
72+
| `ValueChanged` | `ValueChanging` |
73+
| `TextChanged` | `TextChanging` |
74+
| `TitleChanged` | `TitleChanging` |
75+
4576
## See Also
4677

4778
* [Cancellable Work Pattern](cancellable-work-pattern.md) - Conceptual overview

specs/constitution.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Terminal.Gui — Constitution
2+
3+
> The tenets in each section are listed in **precedence order**. When two tenets conflict, the one higher in this document wins.
4+
5+
This document is the single authoritative source for Terminal.Gui's product mission, non-goals, engineering philosophy, and design tenets. All other documents (`CONTRIBUTING.md`, `CLAUDE.md`, `.claude/rules/`, and the deep-dive docs in `docfx/docs/`) elaborate on these tenets; they do not supersede them.
6+
7+
## Table of Contents
8+
9+
- [I. Mission](#i-mission)
10+
- [II. Non-Goals](#ii-non-goals)
11+
- [III. Tenets](#iii-tenets)
12+
- [IV. Engineering Philosophy](#iv-engineering-philosophy)
13+
- [V. Code Style Tenets](#v-code-style-tenets)
14+
- [Relationship to Sub-Projects](#relationship-to-sub-projects)
15+
16+
---
17+
18+
## I. Mission
19+
20+
Terminal.Gui is a **cross-platform UI toolkit for building sophisticated terminal UI (TUI) applications** on .NET. It is the standard by which TUI applications on .NET are measured.
21+
22+
---
23+
24+
## II. Non-Goals
25+
26+
These were considered and rejected — do not accidentally pursue them:
27+
28+
- **Terminal.Gui is not a web framework.** We do not pursue HTML/CSS layout models.
29+
- **Terminal.Gui is not a replacement for ncurses.** We target .NET developers, not C developers.
30+
- **Terminal.Gui is not a pixel renderer.** Width is measured in terminal cells, not pixels.
31+
- **Terminal.Gui is not opinionated about application architecture.** We provide building blocks; we do not mandate MVVM, MVC, or any other application pattern.
32+
- **Terminal.Gui does not own the terminal.** We share it with the host shell and must be good citizens (clean up on exit, respect terminal state).
33+
34+
---
35+
36+
## III. Tenets
37+
38+
### Users Have Final Control
39+
40+
Users choose the platform, the terminal, and the key bindings. Our defaults are consistent and sensible, but everything configurable must be configurable. We never hardcode behavior that the user or developer cannot override. See the [Keyboard deep dive](../docfx/docs/keyboard.md) and [Mouse deep dive](../docfx/docs/mouse.md).
41+
42+
### Keyboard First; Mouse Optional
43+
44+
Terminal users expect full functionality without a mouse. Anything that can be done with the mouse must also be doable with the keyboard. We avoid mouse-only features. See the [Mouse deep dive](../docfx/docs/mouse.md).
45+
46+
### More Editor Than Command Line
47+
48+
Once a Terminal.Gui app starts, the user is no longer using the command line. Users expect keyboard idioms consistent with GUI apps (VS Code, Vim, Emacs, etc.), not shell idioms. See the [Keyboard deep dive](../docfx/docs/keyboard.md).
49+
50+
### Be Consistent With the User's Platform
51+
52+
Users choose their platform. Terminal.Gui apps must respond to keyboard and mouse input in a way consistent with platform conventions. The source of truth for default key bindings is [Wikipedia's keyboard shortcuts table](https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts). See the [Keyboard deep dive](../docfx/docs/keyboard.md).
53+
54+
### If It's Hot, It Works
55+
56+
If a `View` with a `HotKey` is visible and the HotKey is shown, pressing that HotKey must invoke the defined behavior. We strive to ensure that modal contexts do not leave HotKeys appearing active when they are not. See the [Keyboard deep dive](../docfx/docs/keyboard.md).
57+
58+
### Separation of Concerns
59+
60+
Layout, focus, input, and drawing are cleanly decoupled. We resist the urge to merge them for short-term convenience. See the [v2 Architecture overview](../docfx/docs/newinv2.md) and [Layout deep dive](../docfx/docs/layout.md).
61+
62+
### Testability First
63+
64+
Views must be testable in isolation without global state. `Application.Init` is required only for integration tests. We maintain ≥80% test coverage and we never decrease it. See [Testing patterns](../.claude/rules/testing-patterns.md).
65+
66+
### Performance Is a Feature
67+
68+
We measure rendering and event-handling overhead. We never accept regressions in the hot path without a documented justification. See the [Drawing deep dive](../docfx/docs/drawing.md).
69+
70+
### Documentation Is the Spec
71+
72+
API documentation is the contract. When docs and code conflict, the code is wrong. See [api-documentation rules](../.claude/rules/api-documentation.md) and Code Style Tenet 5 in [CONTRIBUTING.md](../CONTRIBUTING.md).
73+
74+
### Think in Graphemes, Not Runes
75+
76+
Text measurement and rendering always operate on grapheme clusters, not `char` or `Rune` values. Always use `string.GetColumns()` for width; always iterate with `GraphemeHelper.GetGraphemes()` for rendering. See [Unicode/Grapheme rules](../.claude/rules/unicode-graphemes.md).
77+
78+
---
79+
80+
## IV. Engineering Philosophy
81+
82+
Developers — AI agents and humans — working on Terminal.Gui strive to raise the bar as Principal Engineers. Principal Engineers are measured by how they live the [Amazon PE Community Tenets](https://www.amazon.jobs/content/en/teams/principal-engineering/tenets):
83+
84+
1. **Exemplary practitioner** — set the standard through your own work.
85+
2. **Technically fearless** — tackle the hardest, most ambiguous problems.
86+
3. **Lead with empathy** — foster inclusion; be mindful of your impact.
87+
4. **Balanced and pragmatic** — neither dogmatic nor reckless.
88+
5. **Illuminate and clarify** — bring clarity to complexity; drive crisp decisions.
89+
6. **Flexible in approach** — adapt style and methods to the problem at hand.
90+
7. **Respect what came before** — appreciate existing systems; learn from the past.
91+
8. **Learn, educate, and advocate** — pursue continuous learning and teach others.
92+
9. **Have resounding impact** — results are the minimum; lasting impact is the bar.
93+
94+
---
95+
96+
## V. Code Style Tenets
97+
98+
*(Source of truth: [CONTRIBUTING.md](../CONTRIBUTING.md))*
99+
100+
1. **Six-Year-Old Reading Level** — Readability over terseness.
101+
2. **Consistency, Consistency, Consistency** — Follow existing patterns ruthlessly.
102+
3. **Don't Be Weird** — Follow Microsoft/.NET conventions.
103+
4. **Set and Forget** — Rely on automated tooling; don't fight the formatter.
104+
5. **Documentation Is the Spec** — API docs define the contract; implementation must match.
105+
106+
---
107+
108+
## Relationship to Sub-Projects
109+
110+
Sub-projects (e.g., `Terminal.Gui.Text`) may extend this constitution. When a sub-project tenet conflicts with a tenet in this document, this document wins unless the sub-project explicitly documents the exception and the reason.

0 commit comments

Comments
 (0)