Skip to content

Fixes #4057 - MASSIVE! Fully implements ColorScheme->Scheme + VisualRole + Colors.->SchemeManager. #4062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 163 commits into
base: v2_develop
Choose a base branch
from

Conversation

tig
Copy link
Collaborator

@tig tig commented Apr 28, 2025

HUGE PR

This PR introduces a significant refactoring of Terminal.Gui's Configuration Management system, addressing issue #4057 and a bunch of other related issues. The changes include:

  • *Significant refactor and upgrade of ConfigurationManager
    • Simplifies and cleans up naming (e.g. SerializableConfigruationProperty -> ConfigProperty).
    • Uses [ModuleInitalizer] to ensure that CM caches the original values of all [ConfigProperty]s and provides APIs for resetting those values back (ConfigurationManager.ResetToHardCodedValues).
    • Removes all lame/complex code that tried to tie CM to Application.Init. Instead CM is simply process-wide.
    • Makes CM OFF BY DEFAULT. Apps must call ConfigurationManager.Enable to enable it.
  • Renames ColorScheme to Scheme. Enables SchemeManager.GetCurrentSchemes() as the API that replaces the old ColorSchemes static class.
  • Introduces VisualRole to replace string-based ColorSchemes[???] identifiers.
  • Changes View.Scheme API signficantly
    • View.ColorScheme -> View.Scheme
    • NEW: View.HasScheme to indicate whether a Scheme was explicitly set or not.
    • NEW: Get/SetScheme (used by Scheme get/set). Enable...
    • NEW: GettingScheme/OnGettingScheme cancellable event
    • NEW: SettingScheme/OnSettingScheme cancellable event
    • GONE: Scheme is no longer virtual (the above addressses).
  • HighlightStyle.Hover is DISABLED in this PR. to be fixed in HighlightStyle.Hover etc... needs a different implementation  #3753.

Much of the vast churn in this PR was done to finally address a slew of non-deterministic unit test problems having to do with static state. There are still some non-CM related issues buried in there, but I've killed most of them here. Note: Any unit test that uses Configuration.Enable MUST NOT be in Parallelizable. However, if you don't call Enable or use Application it's highly likely Parallelizable will work.

IMPORTANT: If you rely on View.Scheme being null ... STOP. Instead use HasScheme.

Fixes

Proposed Changes/Todos

  • Rename ColorScheme to Scheme
  • Introduce VisualRole enum
  • Refactor/Fix ConfigManager to be more robust and thread safe.
  • Refactor CM to make accessing Schemes work better.
  • Replace View.GetXXXColor () with View.GetAttributeForRole ()
  • Add dozens of new unit tests, fix a bunch of old unit tests that were non-deterministic.
  • Ensure new Scheme and Attribute related APIs in View are fully baked and thought through.
  • Make CM.Enable more rational - Have it take ConfigLocation
  • Fix bad implementation of View.Enable in View.Drawing, built-in Views and Scenarios.
  • Update Text Styles scenario to show more
  • Identify and fix why sometimes Style is misapplied/artifacts
  • On Load() "Schemes": [] doesn't accept partial dict. Just overwrites dest.
  • Make sure above addresses ConfigurationManager - It's a bug for any SomeProperty = DefaultSomeProperty if SomeProperty isn't nullable #4021
  • Figure out how to reduce the in-memory size of the config (currently ~126kb!)

Now:

Start: Color size: 4 b
Start: Attribute size: 48 b
Start: Base Scheme size: 2870 b
Start: PropertyInfo size: 48 b
Start: ThemeScope (0) size: 56 b
Start: ThemeScope (1) size: 3020 b
Start: HardCoded Schemes (5) size: 2870 b
Start: Themes dictionary (1) size: 22 Kb
Enabled: Themes dictionary (1) size: 22 Kb
After Load: Themes dictionary (8) size: 47 Kb
Total Settings Size: 52 Kb
  • Fix dynamic theme re-loading in UI Catalog
  • Revamp config.md Deep Dive
  • Add a Schemes Scenario that shows all views and lets you play with applying different schemes
  • Refactor GetViewsUnderMouse to GetViewsUnderLocation etc... to work better
  • Enable the other new VisualRole (Active, HotActive, Highlight)
  • Ensure all API docs are up-to-date
  • Revamp drawing.md Deep Dive
  • Re-do Hover with Highlight
  • Fix spurious unit test failures seen in CI/CD

For Future PRs

Pull Request checklist:

  • I've named my PR in the form of "Fixes #issue. Terse description."
  • My code follows the style guidelines of Terminal.Gui - if you use Visual Studio, hit CTRL-K-D to automatically reformat your files before committing.
  • My code follows the Terminal.Gui library design guidelines
  • I ran dotnet test before commit
  • I have made corresponding changes to the API documentation (using /// style comments)
  • My changes generate no new warnings
  • I have checked my code and corrected any poor grammar or misspellings
  • I conducted basic QA to assure all features are working

tig added 30 commits March 21, 2025 08:22
@tig
Copy link
Collaborator Author

tig commented May 21, 2025

WindowsTerminal_iTXAAkYgrW

@tig
Copy link
Collaborator Author

tig commented May 23, 2025

This is fun. Based on all this, I've been able to significantly improve how transparent shadows are drawn. Previously the shadow was a lame "highlight" of the Normal attribute of the underlying view. And the logic for what the underlying view as lame.

Now, each cell of the shadow actually is a smarter highlight of the underlying cell.

WindowsTerminal_BuWCh8ZW0i

@tig
Copy link
Collaborator Author

tig commented May 27, 2025

I ran an experiment to see how far we could go in having all VisualRole Attributes in a scheme derive from Normal.

E.g. Instead of this:

        Scheme CreateBase ()
        {
            var highlight = new Color ("LightGray");
            highlight = highlight.GetHighlightColor ();

            return new ()
            {
                Normal = new ("LightGray", "RaisinBlack"),
                Focus = new ("White", "DarkGray", "Bold"),
                HotNormal = new ("Silver", "RaisinBlack", "Underline"),
                Disabled = new ("DarkGray", "RaisinBlack", "Faint"),
                HotFocus = new ("White", "DarkGray", "Underline,Bold"),
                Active = new ("White", "Charcoal"),
                HotActive = new ("White", "Charcoal", "Underline"),
                Highlight = new (highlight, new Color ("RaisinBlack")),
                Editable = new ("LightYellow", "OuterSpace")
            };
        }

Do this:

        Scheme CreateBase ()
        {
            var highlight = new Color ("LightGray");
            highlight = highlight.GetHighlightColor ();

            return new ()
            {
                Normal = new ("LightGray", "RaisinBlack"),
            };
        }

(The terseness would apply to Json representation too.

image

Pretty darn good! Now, how will this hold up in other terminals vs WT? I dunno yet.

But I'm going to run with it for now.

@BDisp
Copy link
Collaborator

BDisp commented May 27, 2025

Only the Toplevel Disabled attribute isn't much perceptible.

@tig
Copy link
Collaborator Author

tig commented May 28, 2025

Only the Toplevel Disabled attribute isn't much perceptible.

Yeah.

On this, I keep thinking "why do we have both Base and Toplevel schemes". In what world does anyone want both?

@BDisp
Copy link
Collaborator

BDisp commented May 28, 2025

Yeah.

On this, I keep thinking "why do we have both Base and Toplevel schemes". In what world does anyone want both?

I'm only thinking of one possibility. Maybe I'm wrong but when you define a schema for Window and it's the Application.Top, all other Window sub-views will inherit the same schema, right? If affirmative a user may want a different schema for the Application.Top and other schema for the Window sub-views. In this case it's an advantage having the Toplevel schema. If it's possible to have different schemas for the same Window type then the Toplevel may be removed.

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