Skip to content

Add events for GetNormalColor etc.... #4016

Closed
Closed
@tig

Description

@tig

Related:

Here's the problem

View.GetNormalColor () etc... are virtual, allowing View subclasses the ability to override colors used during draw.

However, if a SuperView wants to influence the colors used by a SubView when they draw all it can do is:

  • Set the SubView's ColorScheme property - This is problematic because now it's next to impossible to keep the SubView's colors in sync with the superview, esp if the superview is ALSO a subview of another view that's trying to do cool color stuff.
  • Try to depend on the fact that ColorScheme is a value type, held by View as by-ref.

One of the motivations for having #457 is the instincts that none of this works very well.

In practice, this "architecture" (saying it was architected is generous) leads to things like:

Shortcut having logic that looks like this in order to control how it's subviews appear in various situations (particularly HasFocus):

if (HasFocus || highlight || ForceFocusColors)
{
    if (_nonFocusColorScheme is null)
    {
        _nonFocusColorScheme = base.ColorScheme;
    }

    base.ColorScheme ??= new (Attribute.Default);

    // When we have focus, we invert the colors
    base.ColorScheme = new (base.ColorScheme)
    {
        Normal = GetFocusColor (),
        HotNormal = GetHotFocusColor (),
        HotFocus = GetHotNormalColor (),
        Focus = GetNormalColor (),
    };
}
else
{
    if (_nonFocusColorScheme is { })
    {
        base.ColorScheme = _nonFocusColorScheme;
        //_nonFocusColorScheme = null;
    }
    else
    {
        base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
    }
}

if (CommandView.Margin is { })
{
    CommandView.Margin.ColorScheme = base.ColorScheme;
}
if (HelpView.Margin is { })
{
    HelpView.Margin.ColorScheme = base.ColorScheme;
}

if (KeyView.Margin is { })
{
    KeyView.Margin.ColorScheme = base.ColorScheme;
}

HighlightStyle.Hover needing logic in View.Mouse like this:

 if (args.NewValue.HasFlag (HighlightStyle.Pressed) || args.NewValue.HasFlag (HighlightStyle.PressedOutside))
 {
     if (_savedHighlightColorScheme is null && ColorScheme is { })
     {
         _savedHighlightColorScheme ??= ColorScheme;

         if (CanFocus)
         {
             var cs = new ColorScheme (ColorScheme)
             {
                 // Highlight the foreground focus color
                 Focus = new (ColorScheme.Focus.Foreground.GetHighlightColor (), ColorScheme.Focus.Background.GetHighlightColo
             };
             ColorScheme = cs;
         }
         else
         {
             var cs = new ColorScheme (ColorScheme)
             {
                 // Invert Focus color foreground/background. We can do this because we know the view is not going to be focus
                 Normal = new (ColorScheme.Focus.Background, ColorScheme.Normal.Foreground)
             };
             ColorScheme = cs;
         }
     }

     // Return false since we don't want to eat the event
     return false;
 }

 if (args.NewValue == HighlightStyle.None)
 {
     // Unhighlight
     if (_savedHighlightColorScheme is { })
     {
         ColorScheme = _savedHighlightColorScheme;
         _savedHighlightColorScheme = null;
     }
 }

Margin needing to do crap like this:

public override ColorScheme? ColorScheme
{
    get
    {
        if (base.ColorScheme is { })
        {
            return base.ColorScheme;
        }

        return (Parent?.SuperView?.ColorScheme ?? Colors.ColorSchemes ["TopLevel"])!;
    }
    set
    {
        base.ColorScheme = value;
        Parent?.SetNeedsDraw ();
    }
}

Solution

After fighting to get focused, highlight, etc... colors working will w/in Shortcut, Bar, etc... as part of Menuv2 I've come up with this concept as a partial fix for #457, focusing on the problem above:

Add cancellable events for each of Normal, HotNormal, Focus, HotFocus

 public virtual Attribute GetNormalColor ()
 {
     Attribute currAttribute = ColorScheme?.Normal ?? Attribute.Default;
     Attribute newAttribute = new Attribute ();
     CancelEventArgs<Attribute> args = new CancelEventArgs<Attribute> (in currAttribute, ref newAttribute);
     GettingNormalColor?.Invoke (this, args);

     if (args.Cancel)
     {
         return args.NewValue;
     }

     ColorScheme? cs = ColorScheme ?? new ();
     Attribute disabled = new (cs.Disabled.Foreground, cs.Disabled.Background);

     if (Diagnostics.HasFlag (ViewDiagnosticFlags.Hover) && _hovering)
     {
         disabled = new (disabled.Foreground.GetDarkerColor (), disabled.Background.GetDarkerColor ());
     }

     return Enabled ? GetColor (cs.Normal) : disabled;
 }

    public event EventHandler<CancelEventArgs<Attribute>>? GettingNormalColor;

This allows Shortcut, for example to get rid of all the SetColor stuff shown above and just do:

      ...
        HelpView.GettingNormalColor += HelpView_GettingNormalColor;
    }

    private void HelpView_GettingNormalColor (object? sender, CancelEventArgs<Attribute>? e)
    {
        if (HasFocus)
        {
            e.Cancel = true;
            e.NewValue = GetFocusColor ();
        }
    }

   // Same for CommandView
   // Since KeyView needs to invert HotNormal and Normal, we create a new class, `KeyView` that overrides `GetNormal/GetHotNormal` instead.

    public override Attribute GetNormalColor ()
    {
        if (HasFocus)
        {
            return base.GetFocusColor ();
        }

        return base.GetNormalColor ();
    }

I will be attempting to implement this as part of

This may also fix:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status

    ✅ Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions