-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Custom popup placement callback #15667
Conversation
I think this PR is a good example of an old discussion where we need to start organizing controls by feature/control rather than putting them all together. In this case I think
Small projects in the MVVM world organize by type, I get that. But inevitably with large projects you end up organizing by project/namespace and then by feature. All large-scale projects I've worked on evolve to organize by feature. |
/// <summary> | ||
/// Defines custom placement parameters for a Popup control. | ||
/// </summary> | ||
public record struct CustomPopupPlacement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also break this out into a new file.
Additionally, if it was me, I would make is a class so it could actually be extended if ever needed. This is too complex of a type to lock-down to a struct in my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see PopupPositionRequest being complex (which is why it's a class already), but why CustomPopupPlacement? Essentially, it's just a three enums and a Point property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my mind struct is for primitives types (int, size, etc.) this is more complex. The reason to make it a class is to not limit yourself in the future (keep the door open for future extension by developers). There also doesn't appear to be a performance reason for this to be a struct.
So my criteria would be
- Everything is a class by default in an object-oriented language
- Is it a primitive type? then make it a struct
- Is it performance critical? then make it a struct.
- Is it a special-case?
It fails both of these struct tests and isn't a special case so my convention would be to make it a class. By default types should be a class in an object oriented language like C# -- not a struct. I do think Avalonia went a little too far with struct usage elsewhere.
I haven't tried the current prototype implementation yet, but my original thought after digging in when writing the issue was that we could improve over WPF's implementation by still allowing default positioning logic to run but also give the callback, if specified, a way to further adjust it. After doing a quick review of the code changes for this PR, this is a summary of the public API changes:
My thoughts on the updates are:
The main reasons that I suggest the changes above are that it keeps the APIs simple and open for possible future expansion. But more importantly, the callback would receive all the pre-initialized anchor/gravity/constraint/offset values and the callback could leave them as-is or adjust them. Consider a case where we set It's very open ended with the suggested changes without requiring the developer to do too much either. I'm happy to discuss further. |
This is a very good idea! |
I might end up wrong here, but positioning API wasn't changed in couple of years, without any need or plans to change it either. Especially when current positioning API is mirroring wayland as a common denominator. I also not sure about reusing the same class (CustomPopupPlacement) for callback input and output. Since input will always include additional parameters - anchor rect and popup size. While these values are never returned from the callback. Marking them readonly helps a little, but is it really worth it?
I am going to leave this PR as is for now, as I am currently on vacation. More opinions are welcomed here. |
You can test this PR using the following package version. |
# Conflicts: # api/Avalonia.nupkg.xml # samples/ControlCatalog/Pages/ToolTipPage.xaml # samples/ControlCatalog/Pages/ToolTipPage.xaml.cs # src/Avalonia.Controls/ToolTip.cs
You can test this PR using the following package version. |
You can test this PR using the following package version. |
Thank you for updating the PR for testing. The related issue (#15233) was created so that we can display What we do in WPF is that any Avalonia has a very different design for This could be somewhat improved if the Another thing I noticed is that in my hacked tests of the new logic, Here's a screenshot showing that constraint adjustment issue. The left side shows where the You can see it displays the I'm available for any continued discussion on this. Thanks! |
That's a good point I missed. I was under assumption there is a way to get placement target outside of this API already.
Popup would be fine, if this callback was only used for popups. But it's also used for tooltips and flyouts. We don't really expose Popup in these controls via other APIs, and we probably shouldn't begin. We can add
From my understanding how Wayland works, this shouldn't be an issue to add. Will push a new commit with some changes. And will update API diff in the first message. |
Pushed a new commit. The biggest change is the callback itself.
it is now
Where CustomPopupPlacement has some get-only parameters (Visual target, Popup size), and the rest are mutable (anchor, offset...). Each mutable field has an actual on a Popup value before the callback. |
You can test this PR using the following package version. |
Thank you for the work on this @maxkatz6. The latest updates are working great for our needs, and we were able to accomplish what we wanted to do with them. From our point of view, feel free to merge this PR. |
/// <summary> | ||
/// Represents a method that provides custom positioning for a <see cref="Popup"/> control. | ||
/// </summary> | ||
public delegate void CustomPopupPlacementCallback(CustomPopupPlacement parameters); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use this pattern rather than an attached event? i.e. why not an attached Popup.RequestCustomPlacement
event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow, ok looks like this comes from WPF. Seems a bit strange not to used an attached event, but looks like there is precedent.
What does the pull request do?
Implements Placement.Custom for Popup and Popup-based controls - Flyout, Tooltip, ContextMenu.
Differences from WPF implementation:
Size targetSize
argument. I don't see any limitations to provide full rect here though.New APIs:
Obsolete APIs changes (no binary breaking changes, except NotClientImplementable interface):
Checklist
Breaking changes
None.
Obsoletions / Deprecations
See API changes.
Fixed issues
Fixes #15233