[Controls] Add CalendarDatePicker inner content slots - #605
Conversation
Add CalendarDatePickerContent attached properties (InnerLeftContent, InnerLeftOfCalendarButtonContent, InnerRightContent), mirroring the ComboBox inner-content slots, and wire them into the MacOS, DevExpress and Linux CalendarDatePicker templates. - MacOS / DevExpress: the calendar button moves into PART_TextBox's InnerRightContent, between the slots. Because that content sits outside the CalendarDatePicker template namescope, the icon/chevron state colours are driven by bindings to $parent[CalendarDatePicker] (and the chevron background by local styles on the border) rather than /template/ styles. - Linux: the date and slots are overlaid on PART_Button as siblings so a slot click does not open the calendar; PART_Button's Min size follows the overlay bounds (with the normal input-height floor) so it always covers the slots. - SampleApp: demonstrate the three slots on the CalendarDatePicker page.
There was a problem hiding this comment.
Pull request overview
Adds three CalendarDatePicker content slots across the MacOS, DevExpress, and Linux themes.
Changes:
- Introduces attached properties for the three slots.
- Updates themes and TextBox padding overrides.
- Adds an interactive SampleApp demonstration.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
MacOS/Controls/TextBox.axaml |
Makes slot padding overrideable. |
MacOS/Controls/CalendarDatePicker.axaml |
Integrates slots and calendar button. |
Linux/Controls/CalendarDatePicker.axaml |
Adds an overlaid slot layout. |
DevExpress/Controls/TextBox.axaml |
Makes slot padding overrideable. |
DevExpress/Controls/CalendarDatePicker.axaml |
Integrates slots and chevron states. |
AttachedProperties/CalendarDatePickerContent.cs |
Defines the slot API. |
CalendarDatePickerViewModel.cs |
Adds demo commands and state. |
CalendarDatePickerDemo.axaml |
Demonstrates the new slots. |
Comments suppressed due to low confidence (2)
samples/SampleApp/DemoPages/CalendarDatePickerDemo.axaml:71
- This color-only button has no accessible name, so assistive technology cannot communicate that it selects red. Add an explicit automation name.
<Button Command="{Binding RedCommand}"><Border Width="8" Height="8" Background="Red" /></Button>
samples/SampleApp/DemoPages/CalendarDatePickerDemo.axaml:68
- This color-only button has no accessible name, so assistive technology cannot communicate that it selects orange. Add an explicit automation name.
<Button Command="{Binding OrangeCommand}"><Border Width="8" Height="8" Background="Orange" /></Button>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉
Integration Details
{
"deliveryId": "31875d50-8b52-11f1-9cd1-e3fa99782534",
"headSha": "dff2b14fa5bc8434451ca4341e4d1aef1d1deb44",
"reviewer": "copilot-pull-request-reviewer[bot]"
}|
Anna Malchow-Perryman (@apman) For this one, I'd still rather have a review from you since I'm touching the themes of already existing controls. |
Anna Malchow-Perryman (apman)
left a comment
There was a problem hiding this comment.
Looks good for the most part. Just a couple of changes I noticed:
The TextBox keeps focus when the calendar view is opened. Functionally that doesn't make much of a difference to before, because we don't have keyboard navigation for the calendar popup yet anyway. But maybe keeping the focus in the box makes that situation less clear (e.g. when days happen to be selected in the TextBox and you in-/decrease them with the arrow keys, it appears as if the popup calendar is responding to keyboard input as it updates based on the TextBox values (but that's not working for year or month).
Also, when you tab while the popup is open, the focus moves to the next without closing the current one (so the focussed control is hidden by the previously opened one)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/Devolutions.AvaloniaControls/AttachedProperties/CalendarDatePickerContent.cs:44
- The lazily returned
AvaloniaListcan be mutated after the template is applied, but each theme computes slot visibility by converting only the attached-property value. Collection changes do not change that property value, so adding the first item leaves itsItemsControlcollapsed (and removing the last leaves its spacing visible). Drive visibility from eachItemsControl's collection-aware:emptystate (or another item-count notification) instead.
AvaloniaList<Control> list = [];
There was a problem hiding this comment.
🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉
Integration Details
{
"deliveryId": "e844b050-8b7b-11f1-986c-132840bd9384",
"headSha": "539ba4cb984a3aed3810702ac8fcfb5ee4210a50",
"reviewer": "copilot-pull-request-reviewer[bot]"
}
True! I didn't notice this in my test. I'll look into fixing both. |
Moving the calendar button into PART_TextBox (to host the inner-content slots) trapped keyboard focus in the text box when the popup opened: the box kept focus (its segmented arrow-keys looked like calendar input) and Tab no longer closed the popup. Restore the original behaviour: - CalendarDatePickerBehavior focuses the calendar when the popup opens (so focus leaves the text box, and Avalonia's light-dismiss closes it on Tab again) and returns focus to the picker's text box (or the button on Yaru, where the text box is hidden) when it closes, keeping the normal tab order. - PART_Calendar is marked Focusable="True" in the MacOS, DevExpress and Linux templates so that Focus() takes effect.
|
Anna Malchow-Perryman (@apman) I believe I've covered everything with the last three commits. The plus side of this, now, just by focusing the Calendar when shown, we get in bonus the keyboard navigation in it. I've notice one more bug, which is not a regression though (it was already a bug before my changes). Tab navigation doesn't work with CalendarDatePicker in the macOS theme. That is, one a CalendarDatePicker gets the focus, it never relinquishes it again through tab key press. I did not address this as par of this PR. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/Devolutions.AvaloniaControls/Behaviors/CalendarDatePickerBehavior.cs:62
- This unconditionally re-focuses the picker after every close. When light-dismiss is caused by clicking another focusable control, the click first focuses that control, then this background callback steals focus back to the picker. Only restore focus when it is still in the popup (or no element received focus); preserve focus established by an external click.
Dispatcher.UIThread.Post(() => ReturnFocusToPicker(datePicker), DispatcherPriority.Background);
src/Devolutions.AvaloniaControls/AttachedProperties/CalendarDatePickerContent.cs:45
- The returned list is mutable, but each theme computes slot visibility from the attached-property value only. Mutating this
AvaloniaListraises collection changes, not an attached-property change, so an initially empty slot remains collapsed after its first item is added (and removing its last item does not re-collapse it). Make visibility observe collection changes, or structure the templates so emptyItemsControls do not require a convertedIsVisiblebinding.
AvaloniaList<Control> list = [];
element.SetValue(property, list);
src/Devolutions.AvaloniaTheme.Linux/Controls/CalendarDatePicker.axaml:75
PART_Buttonis the focusable Linux picker surface, but it no longer has content or an automation name. Its automation peer therefore loses the selected-date/placeholder text that previously came fromButton.Content, leaving screen-reader users on an unnamed button. BindAutomationProperties.Nameto the same first-non-empty text while keeping the visual text in the overlay.
<Button Name="PART_Button"
Foreground="{Binding Path=#PART_TextBox.Foreground}"
MinWidth="{Binding #SlotOverlay.Bounds.Width}"
MinHeight="{Binding #SlotOverlay.Bounds.Height}" />
There was a problem hiding this comment.
🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉
Integration Details
{
"deliveryId": "25ab89f0-8b8b-11f1-8bcf-561651bef420",
"headSha": "8d1cfdf6b56ae84e651d4b27a14bdbe89883a094",
"reviewer": "copilot-pull-request-reviewer[bot]"
}
Anna Malchow-Perryman (apman)
left a comment
There was a problem hiding this comment.
I've notice one more bug, which is not a regression though (it was already a bug before my changes). Tab navigation doesn't work with CalendarDatePicker in the macOS theme. That is, one a CalendarDatePicker gets the focus, it never relinquishes it again through tab key press. I did not address this as par of this PR.
Xavier Fortin (@xfortin-devolutions) - yes, and in DevExpress tabbing only works forward ... also didn't mention that since it wasn't due to your changes. I can make a note to address both some time.
Thanks for fixing the other stuff!
|
Anna Malchow-Perryman (@apman) My pleasure! |
Summary
Adds inner content slots to
CalendarDatePicker, mirroring the existingComboBoxinner-content slots, so consumers can drop icons / badges / buttons into the control without subclassing.New attached-property class
Devolutions.AvaloniaControls.AttachedProperties.CalendarDatePickerContentwith three slots:InnerLeftContent— before the date textInnerLeftOfCalendarButtonContent— between the date and the calendar buttonInnerRightContent— after the calendar buttonWired into all three themes (MacOS, DevExpress, Linux).
Implementation notes
PART_TextBox'sInnerRightContent, between the slots. Because that content sits outside theCalendarDatePickertemplate namescope, the icon / chevron state colours are driven by$parent[CalendarDatePicker]bindings (and the DevExpress chevron background by local styles on the border) rather than/template/styles, which cannot reach it.PART_Buttonas siblings, so a slot click bubbles up the overlay instead of opening the calendar;PART_Button's min size follows the overlay bounds (with the normal input-height floor) so it always covers the slots./template/style setter instead of a direct attribute, so an instance can override it. The picker always assigns inner content, which would otherwise leave the presenters padding the layout even when a slot is empty; the picker now locally zeroes that padding. No visual change to regular TextBoxes.Testing
Built the solution and manually verified each theme in the SampleApp: