Skip to content

BREAKING CHANGE: Fixes #5366. Make View.Text notifications CWP-compliant and non-virtual#5371

Open
tig wants to merge 12 commits into
developfrom
tig/tig-issue-5366-view-text-cwp-compliant
Open

BREAKING CHANGE: Fixes #5366. Make View.Text notifications CWP-compliant and non-virtual#5371
tig wants to merge 12 commits into
developfrom
tig/tig-issue-5366-view-text-cwp-compliant

Conversation

@tig
Copy link
Copy Markdown
Member

@tig tig commented May 22, 2026

Summary

Makes View.Text notifications follow the Cancellable Work Pattern (CWP) and removes virtual from the Text property, migrating all 17 subclass overrides.

Changes

CWP Events (Commit 1)

  • TextChanging (EventHandler<CancelEventArgs>?): Cancellable pre-change event (signal-only, no text values in args per issue spec)
  • TextChanged (EventHandler?): Post-change notification (unchanged signature, now raised via CWP flow)
  • OnTextChanging() (protected virtual bool): Override point; return true to cancel
  • OnTextChanged() (protected virtual void): Override point for post-change logic

Non-Virtual Migration (Commit 2)

  • Removed virtual from View.Text property
  • Added SetTextDirect(string value) for derived views to update base _text without re-entering CWP
  • Migrated all 17 overrides using appropriate patterns:
    • Simple views (Label, Button, CheckBox, TitleView, Code, Shortcut): OnTextChanged() override
    • Domain adapters (DatePicker, ColorPicker, ProgressBar, ArrangerButton, LinearRangeViewBase): OnTextChanged() + SetTextDirect()
    • Markdown views: OnTextChanged() + UpdateTextFormatterText() override
    • TextField/TextView: new keyword (independent storage) + SetTextDirect()
    • TextValidateField: OnTextChanged() + SetTextDirect() after provider transforms

Breaking Changes

  • View.Text is no longer virtual — subclasses cannot override it
  • OnTextChanged() changed from public void to protected virtual void
  • New TextChanging event may cause unexpected cancellation if subscribed

Testing

  • 9 new CWP unit tests added
  • All 17,081 parallelizable tests pass
  • All 74 non-parallelizable tests pass

Fixes #5366

Add signal-only CWP-compliant text-change workflow to View.Text:

- Add protected virtual bool OnTextChanging() pre-change hook
- Add public event EventHandler<CancelEventArgs>? TextChanging
- Refactor OnTextChanged() from public void to protected virtual void
  (raises TextChanged event; subclasses can override)
- Text setter now follows CWP flow: early-return on same value,
  OnTextChanging, TextChanging event, mutate, OnTextChanged
- Canceling TextChanging prevents text mutation and suppresses TextChanged
- Add 'new' keyword to TextField.TextChanging (intentional hiding with
  richer ResultEventArgs<string> semantics)
- Add comprehensive unit tests for CWP behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c316b31a44

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Terminal.Gui/ViewBase/View.Text.cs Outdated
@tig tig marked this pull request as draft May 22, 2026 16:48
… CWP events

Remove �irtual from View.Text property and add SetTextDirect() for
derived views to update base text without re-entering CWP flow.

Migration patterns:
- Simple views (Label, Button, CheckBox, TitleView, Code, Shortcut):
  Override OnTextChanged() to sync with internal models.
- Domain adapters (DatePicker, ColorPicker, ProgressBar, ArrangerButton,
  LinearRangeViewBase): Override OnTextChanged() for parsing + SetTextDirect()
  for reverse sync.
- Markdown views: Override OnTextChanged() + UpdateTextFormatterText() to
  suppress raw text rendering.
- TextField/TextView: Use 'new' keyword (independent storage) + SetTextDirect()
  to keep base in sync.
- TextValidateField: Override OnTextChanged() + SetTextDirect() after provider
  transforms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tig tig changed the title BREAKING CHANGE: Fixes #5366. Make View.Text notifications CWP-compliant BREAKING CHANGE: Fixes #5366. Make View.Text notifications CWP-compliant and non-virtual May 22, 2026
@tig tig marked this pull request as ready for review May 22, 2026 17:31
@tig tig marked this pull request as draft May 22, 2026 17:31
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ad0d8a869

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Terminal.Gui/Views/TextInput/TextField/TextField.Text.cs Outdated
Comment thread Terminal.Gui/Views/TextInput/TextView/TextView.Text.cs
Comment thread Terminal.Gui/Views/ProgressBar.cs Outdated
tig and others added 9 commits May 22, 2026 11:40
- ReactiveExample: Replace .Events().TextChanged on TextField with
  Observable.FromEventPattern to avoid ReactiveMarbles source generator
  producing conflicting handler code for the new/base TextChanging
  event pair (CS0123)
- DateEditor/TimeEditor: Fix CS1574 by changing cref from
  TextValidateField.Text to View.Text

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…llback

1. OnTextChanging() now raises TextChanging event (CWP: virtual raises event)
2. TextField: override OnTextChanged() to sync internal _text from base.Text
   when set polymorphically via a View reference
3. TextView: override OnTextChanged() to sync internal TextModel from base.Text
   when set polymorphically via a View reference
4. ProgressBar: initialize Text to '0%' in constructor so SimplePlusPercentage
   renders on first draw

Added 4 regression tests (verified failing before fixes, passing after).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TextField's
ew Text setter now calls �ase.OnTextChanging() after its
own validation passes (ValueChanging, TextField.TextChanging), so subscribers
holding a View reference see the pre-change notification and can cancel.

TextView's
ew Text setter now calls OnTextChanging() at the start,
so View.TextChanging is raised before the model is modified.

This ensures both controls participate in the base-level CWP flow when
their Text property is set directly (not just polymorphically).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TextField now uses View.Text directly instead of hiding it with 'new'.
This fixes the polymorphism issue: setting Text through a View reference
now correctly goes through TextField's validation and model sync.

Changes:
- View.OnTextChanging now accepts 'string proposedValue' so overrides
  can validate the incoming text
- TextField overrides OnTextChanging to sanitize (strip tabs/newlines),
  fire IValue<string>.ValueChanging, and fire TextField.TextChanging
- TextField overrides OnTextChanged to sync the internal grapheme-list
  model, track history, fire ValueChanged, and adjust cursor
- ProgressBar.OnTextChanging updated for new signature
- TextView.OnTextChanging call updated for new signature

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The event is now raised directly inside the CWP override, as intended.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces direct field and UI updates in the View.Text property setter with a call to SetTextDirect(value). This centralizes text update logic, improving maintainability and reducing code duplication.
Reset TextField.Text.cs to original member order and apply only
semantic changes (CWP overrides, remove RaiseTextChanging, shadow
TextChanging event). Revert cosmetic change in TextField.Mouse.cs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the Cancellable Work Pattern (CWP) for View.Text notifications (adding cancellable TextChanging, making TextChanged part of a CWP flow), and makes View.Text non-virtual by migrating existing overrides to OnTextChanged/OnTextChanging + SetTextDirect.

Changes:

  • Added CWP-compliant TextChanging (cancellable) and refactored TextChanged raising to follow CWP hooks in View.Text.
  • Removed virtual from View.Text, introducing SetTextDirect and migrating derived views (including Markdown, ProgressBar, and text input views).
  • Added new unit tests covering base CWP behavior and several regression scenarios (including polymorphic View.Text sets on TextField/TextView).

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Tests/UnitTestsParallelizable/ViewBase/TextCwpTests.cs Adds unit tests for View.Text CWP semantics and regressions.
Tests/UnitTestsParallelizable/ViewBase/Keyboard/KeyboardEventTests.cs Updates a test helper to hide (not override) Text after View.Text becomes non-virtual.
Terminal.Gui/Views/TextInput/TimeEditor.cs Fixes XML doc reference to View.Text.
Terminal.Gui/Views/TextInput/TextView/TextView.Text.cs Migrates TextView.Text to new + uses CWP hooks and sync behavior.
Terminal.Gui/Views/TextInput/TextView/TextView.cs Initializes TextView model/history to a consistent empty state.
Terminal.Gui/Views/TextInput/TextValidateField.cs Migrates TextValidateField from overriding Text to syncing via OnTextChanged/SetTextDirect.
Terminal.Gui/Views/TextInput/TextField/TextField.Text.cs Refactors TextField text workflow to use base View.Text CWP with internal model syncing.
Terminal.Gui/Views/TextInput/TextField/TextField.Mouse.cs Removes unused return value from a helper call.
Terminal.Gui/Views/TextInput/DateEditor.cs Fixes XML doc reference to View.Text.
Terminal.Gui/Views/Shortcut.cs Migrates help text propagation from overriding Text to OnTextChanged + SetTextDirect.
Terminal.Gui/Views/ProgressBar.cs Keeps Text synced with percentage and restricts external Text changes via OnTextChanging.
Terminal.Gui/Views/Markdown/MarkdownTable.cs Migrates table parsing/sync to OnTextChanged and keeps Text aligned with TableData.
Terminal.Gui/Views/Markdown/MarkdownCodeBlock.cs Migrates parsing to OnTextChanged and prevents raw text rendering via formatter clearing.
Terminal.Gui/Views/Markdown/Markdown.cs Migrates markdown updates to OnTextChanged and prevents raw text rendering via formatter clearing.
Terminal.Gui/Views/LinearRange/LinearRangeViewBase.cs Migrates CSV parsing to OnTextChanged and keeps Text synced with Options.
Terminal.Gui/Views/Label.cs Migrates Text/Title coupling to OnTextChanged + SetTextDirect.
Terminal.Gui/Views/DatePicker.cs Migrates value parsing to OnTextChanged and syncs formatted text via SetTextDirect.
Terminal.Gui/Views/Color/ColorPicker.cs Migrates color parsing to OnTextChanged and syncs formatted text via SetTextDirect.
Terminal.Gui/Views/Code.cs Migrates styled-line updates to OnTextChanged.
Terminal.Gui/Views/CheckBox.cs Migrates Text/Title coupling to OnTextChanged + SetTextDirect.
Terminal.Gui/Views/Button.cs Migrates Text/Title coupling to OnTextChanged + SetTextDirect.
Terminal.Gui/ViewBase/View.Text.cs Implements TextChanging, makes Text non-virtual, adds SetTextDirect and RaiseTextChanged.
Terminal.Gui/ViewBase/Adornment/TitleView.cs Migrates Text/Title coupling to OnTextChanged.
Terminal.Gui/ViewBase/Adornment/ArrangerButton.cs Removes Text override; derives glyph text via SetTextDirect on ButtonType changes.
Examples/ReactiveExample/LoginView.cs Updates Rx subscriptions to TextChanged using FromEventPattern.

Comment thread Terminal.Gui/Views/TextInput/TextView/TextView.Text.cs Outdated
Comment thread Terminal.Gui/Views/TextInput/TextField/TextField.Text.cs
Comment thread Terminal.Gui/Views/TextInput/TextValidateField.cs Outdated
Comment thread Terminal.Gui/Views/DatePicker.cs
Comment thread Terminal.Gui/Views/Color/ColorPicker.cs
Comment thread Terminal.Gui/ViewBase/View.Text.cs
@tig tig marked this pull request as ready for review May 23, 2026 22:44
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f53a59ea1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Terminal.Gui/Views/TextInput/TextValidateField.cs Outdated
Comment thread Terminal.Gui/Views/DatePicker.cs
Comment thread Terminal.Gui/Views/Color/ColorPicker.cs
…Changed

- TextValidateField: move ValueChanging cancellation from OnTextChanged to
  OnTextChanging so rejected edits never reach View.Text
- DatePicker: add OnTextChanging override rejecting unparseable dates
- ColorPicker: add OnTextChanging override rejecting unparseable colors
- TextField: clear _pendingText when base.OnTextChanging cancels
- TextView: add _ownSetterActive flag to skip redundant model sync
- View.Text: change RaiseTextChanged from internal to protected internal
- Add 5 regression tests in TextCwpTests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@BDisp BDisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me. However, it's necessary to ensure consistency between the altered text value and any other property that might be directly linked to it, such as converting that property's value to match the string value. To ensure this, unit tests in these cases simply need to guarantee the correct value based on whether or not the value of that property is accepted and synchronized with the text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make View.Text notifications CWP-compliant

3 participants