Skip to content
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

[v2] New Control - SettingsExpander #211

Merged
merged 13 commits into from
Oct 21, 2022
Merged

[v2] New Control - SettingsExpander #211

merged 13 commits into from
Oct 21, 2022

Conversation

amwx
Copy link
Owner

@amwx amwx commented Oct 16, 2022

Technically not entirely new as this existed in my Sample app albeit with a different name, but I'm revamping it before I start the upcoming facelift to the sample app and moving it to the main controls library as it may be useful to others.

Introducing the SettingsExpander control (which can be seen in the Windows 11 settings app, for example). Some inspirations for the new design come from here, though its not entirely the same.

image

In addition, I've also added a new control FABorder. Part of the design of the SettingsExpander requires the background of a Border to be on the inner edge of the border. Avalonia's border rendering is to place the background in the middle of the stroke - assuming uniform corner radius. This is very noticeable with the translucent stroke colors in Fluent v2. So, I've ported the BackgroundSizing property from UWP to FABorder to help this situation. Note that while FABorder inherits from Border, the pen properties that were recently added (dash style, etc) are not and will not be supported.

As this is still a WIP, I'll have more on the API design later. I can say though, that SettingsExpander is an ItemsControl and is being built to work with binding items - so it's not a Xaml/Code only control.

API & design:

// TopLevel SettingsExpander - these can hold items and expand/collapse them
public partial class SettingsExpander : HeaderedItemsControl
{ 
    // Gets or sets the Header template for the SettingsExpander
    public IDataTemplate HeaderTemplate { get; set; }

    // Gets or sets the description text
    public string Description { get; set; }

    // Gets or sets the IconSource for the SettingsExpander
    public IconSource IconSource { get; set; }

    // Gets or sets the Footer content for the SettingsExpander
    public object Footer { get; set; }

    // Gets or sets the Footer template for the SettingsExpander
    public IDataTemplate FooterTemplate { get; set; }

    // Gets or sets whether the SettingsExpander is currently expanded
    public bool IsExpanded { get; set; }

    // Gets or sets the Action IconSource when <see cref="IsClickEnabled"/> is true
    public IconSource ActionIconSource { get; set; }

    // Gets or sets whether the item is clickable which can be used for navigation within an app
    // This property can only be set if no items are added to the SettingsExpander. Attempting to mark
    // a settings expander clickable and adding child items will throw an exception
    public bool IsClickEnabled { get; set; }

    // Gets or sets the Command that is invoked upon clicking the item
    public ICommand Command { get; set; }

    // Gets or sets the command parameter
    public object CommandParameter { get; set; }

    // (Routed) Event raised when the SettingsExpander is clicked and IsClickEnabled = true
    public event EventHandler<RoutedEventArgs> Click;
}
// SettingsExpanderItem - the child items placed within a SettingsExpander
public class SettingsExpanderItem : ContentControl
{
// These have all the same properties listed above EXCEPT:
// Notice this is now a ContentControl - use Content for child items instead of Header - nesting items here is not supported
// As a result, there is no IsExpanded property
}

Adaptive Width Trigger:
When a SettingsExpander or SettingExpanderItem gets too small, the footer content will be moved below the header/content & description and left-aligned to ensure content is displayed nicely. The default width is 460, smaller will trigger this state to occur. This is customizable by overriding the resource SettingsExpanderItemAdaptiveWidthTrigger

IsClickEnabled
Noted above in the comment for IsClickEnabled, you cannot set child items and IsClickEnabled to true. Doing so will throw and exception.

ActionIconSource property
When IsClickEnabled is true, you can specify the ActionIconSource to provide an icon on the right side of the control (in LTR) to indicate clicking will do some sort of action. There is no default icon provided here. If you want a chevron, use a SymbolIconSource with Symbol=ChevronRight. If you want the icon like in the screenshot above (the last child item under AccentColor), use a FontIconSource with the FontFamily set to SymbolThemeFontFamily and glyph &#xE8A7

TODO:

  • Finish API design
    - [x] Expand/Collapse animation
  • Add resources where necessary
  • XML Comments
  • Testing with bindings
  • Test command/click events work

Edit:
I've decided NOT to add the expand/collapse animation to the SettingsExpander, in my testing it just didn't look that great. You can still set the ContentTransition property on the inner Expander through a custom style selector, however.

@amwx amwx added enhancement New feature or request v2 Related to v2 labels Oct 16, 2022
@alex6dj
Copy link

alex6dj commented Oct 16, 2022

Just a suggestion. I think the Header shoul be centered if Description isnt provided, didnt tested. The behaviour of the old setting controls was to left the header on top.

@amwx
Copy link
Owner Author

amwx commented Oct 16, 2022

Just a suggestion. I think the Header shoul be centered if Description isnt provided, didnt tested. The behaviour of the old setting controls was to left the header on top.

It is. You can see it in the child items within AccentColor in the screenshot above, or for just for case in a top-level SettingsExpander:
image

@alex6dj
Copy link

alex6dj commented Oct 16, 2022

Ok, well done. I just write without checking the code, a bad habit.
Another thing I noticed the Header property use IDataTemplate so it can use others controls inside, right, like a custom hyperlink control.

@amwx
Copy link
Owner Author

amwx commented Oct 17, 2022

Ok, well done. I just write without checking the code, a bad habit. Another thing I noticed the Header property use IDataTemplate so it can use others controls inside, right, like a custom hyperlink control.

Yes. This is being built to support binding/data templates. Of course the original Windows design as far as I can tell is meant for strings, but why not give more control if you desire it. On SettingsExpander, Header is an object with the corresponding HeaderTemplate property; and SettingsExpanderItem (the child items) is a normal ContentControl so you can set Content and ContentTemplate accordingly.

@robloo
Copy link
Contributor

robloo commented Oct 17, 2022

Should BackgroundSizing (although I never really liked the name) be added to upstream Avalonia? I've also seen it needed in a few cases and there is no solution for it right now. I think the need will grow in the future as more people pick up Avalonia and start customizing backgrounds/borders like you are doing here.

I would help with any implementation and can update control templates.

@amwx
Copy link
Owner Author

amwx commented Oct 17, 2022

Should BackgroundSizing (although I never really liked the name) be added to upstream Avalonia?

I'm sure it would be helpful to someone, however, I don't want to be the one to do it.
If someone else wants to use my copy as a guide that's fine, though I've made some significant changes to the BorderRenderHelper class which I've noted in the code, but:
1 - I changed to cache the property values (Background, BorderBrush, Padding, etc) trading off that little bit of memory over doing multiple StyledProperty reads every OnRender call (even if the property doesn't change)
2 - As I said above, I removed support for the Pen properties (Dashes, LineCap, LineJoin) as IMO those don't belong on Border anyway. They also don't apply when the complex render path is used which means inconsistent render behavior within the control
3 - Changed one of the helper classes to be a readonly struct instead of a class
4 - Added a class to store the two geometries for the complex render path instead of storing them both on the BorderRenderHelper

@amwx amwx marked this pull request as ready for review October 18, 2022 22:39
@amwx amwx changed the title [WIP] [v2] New Control - SettingsExpander [v2] New Control - SettingsExpander Oct 18, 2022
@amwx amwx merged commit afbb840 into master Oct 21, 2022
@amwx amwx deleted the v2_SettingsExpander branch October 21, 2022 19:05
@amwx amwx mentioned this pull request Nov 9, 2022
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request v2 Related to v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants