-
Notifications
You must be signed in to change notification settings - Fork 7.5k
feat: Replace Quick Accent toolbar position dropdown with visual 3x3 grid #44113
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
base: main
Are you sure you want to change the base?
feat: Replace Quick Accent toolbar position dropdown with visual 3x3 grid #44113
Conversation
…grid Closes microsoft#44111 Replaced the ComboBox dropdown for toolbar position selection with a visual 3x3 grid of tiles, making it easier to select positions intuitively. Changes: - Added IndexToBoolConverter for int-to-bool binding conversion - Created PositionRadioButtonStyle with accent color highlighting - Replaced ComboBox with 3x3 Grid of styled RadioButtons - Each tile shows position with tooltip for accessibility The grid layout visually represents the 9 toolbar positions: +----+----+----+ | TL | TC | TR | +----+----+----+ | L | C | R | +----+----+----+ | BL | BC | BR | +----+----+----+
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.
Pull request overview
This PR replaces the ComboBox dropdown for Quick Accent toolbar position selection with an intuitive visual 3×3 grid of RadioButtons, making it easier for users to understand and select where the accent toolbar appears on screen.
Key changes:
- Created
IndexToBoolConverterto enable two-way binding between an integer index and RadioButton selection - Replaced dropdown ComboBox with visual 3×3 grid where each position is represented spatially
- Added custom
PositionRadioButtonStylewith accent color highlighting for selected positions
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/settings-ui/Settings.UI/Converters/IndexToBoolConverter.cs | New converter for binding integer indices to RadioButton IsChecked state; enables bidirectional sync between ViewModel.ToolbarPositionIndex and 9 RadioButtons |
| src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml | Replaces ComboBox with 3×3 grid layout, adds converter resource and custom RadioButton style with visual states |
| // Return -1 to indicate no change (RadioButton unchecked) | ||
| return -1; |
Copilot
AI
Dec 6, 2025
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.
Returning -1 when a RadioButton is unchecked could potentially corrupt the ToolbarPositionIndex if the framework calls ConvertBack during automatic RadioButton deselection. Consider returning DependencyProperty.UnsetValue instead to explicitly indicate no update should occur. For reference: using Microsoft.UI.Xaml; and return DependencyProperty.UnsetValue;
| <VisualState.Setters> | ||
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorSecondaryBrush}"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| <VisualState x:Name="Pressed"> | ||
| <VisualState.Setters> | ||
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}"/> | ||
| </VisualState.Setters> | ||
| </VisualState> |
Copilot
AI
Dec 6, 2025
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.
The PointerOver and Pressed visual states will override the Checked state's background, making it unclear that the button is selected when hovering or pressing an already-checked button. Consider adding conditions to preserve the accent color when the button is checked. Example: use VisualState.StateTriggers or only change the background for unchecked buttons in hover/pressed states.
| <VisualState.Setters> | |
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorSecondaryBrush}"/> | |
| </VisualState.Setters> | |
| </VisualState> | |
| <VisualState x:Name="Pressed"> | |
| <VisualState.Setters> | |
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}"/> | |
| </VisualState.Setters> | |
| </VisualState> | |
| <VisualState.Setters> | |
| <!-- Only apply hover background if not checked --> | |
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorSecondaryBrush}"/> | |
| </VisualState.Setters> | |
| </VisualState> | |
| <VisualState x:Name="PointerOverChecked"> | |
| <VisualState.Setters> | |
| <!-- Keep accent color when checked and hovered --> | |
| <Setter Target="RootGrid.Background" Value="{ThemeResource AccentFillColorDefaultBrush}"/> | |
| </VisualState.Setters> | |
| </VisualState> | |
| <VisualState x:Name="Pressed"> | |
| <VisualState.Setters> | |
| <!-- Only apply pressed background if not checked --> | |
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}"/> | |
| </VisualState.Setters> | |
| </VisualState> | |
| <VisualState x:Name="PressedChecked"> | |
| <VisualState.Setters> | |
| <!-- Keep accent color when checked and pressed --> | |
| <Setter Target="RootGrid.Background" Value="{ThemeResource AccentFillColorDefaultBrush}"/> | |
| </VisualState.Setters> | |
| </VisualState> |
| <VisualState.Setters> | ||
| <Setter Target="RootGrid.Background" Value="{ThemeResource ControlFillColorTertiaryBrush}"/> | ||
| </VisualState.Setters> | ||
| </VisualState> |
Copilot
AI
Dec 6, 2025
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.
The RadioButton template is missing a Disabled visual state. Since the SettingsGroup can be disabled (via IsEnabled binding), the RadioButtons should visually indicate when they're disabled. Add a VisualState for the Disabled state in the CommonStates group to show reduced opacity or different styling when disabled. Example:
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="RootGrid.Opacity" Value="0.4"/>
</VisualState.Setters>
</VisualState>| </VisualState> | |
| </VisualState> | |
| <VisualState x:Name="Disabled"> | |
| <VisualState.Setters> | |
| <Setter Target="RootGrid.Opacity" Value="0.4"/> | |
| </VisualState.Setters> | |
| </VisualState> |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=4}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Top right corner" | ||
| ToolTipService.ToolTip="Top right corner"/> | ||
|
|
||
| <!-- Left (index 2) --> | ||
| <RadioButton Grid.Row="1" Grid.Column="0" GroupName="ToolbarPosition" | ||
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=2}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Left" | ||
| ToolTipService.ToolTip="Left"/> | ||
| <!-- Center (index 8) --> | ||
| <RadioButton Grid.Row="1" Grid.Column="1" GroupName="ToolbarPosition" | ||
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=8}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Center" | ||
| ToolTipService.ToolTip="Center"/> | ||
| <!-- Right (index 3) --> | ||
| <RadioButton Grid.Row="1" Grid.Column="2" GroupName="ToolbarPosition" | ||
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=3}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Right" | ||
| ToolTipService.ToolTip="Right"/> | ||
|
|
||
| <!-- Bottom Left (index 7) --> | ||
| <RadioButton Grid.Row="2" Grid.Column="0" GroupName="ToolbarPosition" | ||
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=7}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Bottom left corner" | ||
| ToolTipService.ToolTip="Bottom left corner"/> | ||
| <!-- Bottom Center (index 1) --> | ||
| <RadioButton Grid.Row="2" Grid.Column="1" GroupName="ToolbarPosition" | ||
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=1}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Bottom center" | ||
| ToolTipService.ToolTip="Bottom center"/> | ||
| <!-- Bottom Right (index 6) --> | ||
| <RadioButton Grid.Row="2" Grid.Column="2" GroupName="ToolbarPosition" | ||
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=6}" | ||
| Style="{StaticResource PositionRadioButtonStyle}" | ||
| AutomationProperties.Name="Bottom right corner" | ||
| ToolTipService.ToolTip="Bottom right corner"/> |
Copilot
AI
Dec 6, 2025
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.
The tooltip and AutomationProperties should use localized strings via x:Uid instead of hardcoded English text. The existing localization keys from the ComboBoxItems should be reused. For example, use x:Uid="QuickAccent_ToolbarPosition_TopLeftCorner" on the RadioButton instead of hardcoded AutomationProperties.Name and ToolTipService.ToolTip. This ensures the UI is properly localized for all supported languages.
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=4}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Top right corner" | |
| ToolTipService.ToolTip="Top right corner"/> | |
| <!-- Left (index 2) --> | |
| <RadioButton Grid.Row="1" Grid.Column="0" GroupName="ToolbarPosition" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=2}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Left" | |
| ToolTipService.ToolTip="Left"/> | |
| <!-- Center (index 8) --> | |
| <RadioButton Grid.Row="1" Grid.Column="1" GroupName="ToolbarPosition" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=8}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Center" | |
| ToolTipService.ToolTip="Center"/> | |
| <!-- Right (index 3) --> | |
| <RadioButton Grid.Row="1" Grid.Column="2" GroupName="ToolbarPosition" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=3}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Right" | |
| ToolTipService.ToolTip="Right"/> | |
| <!-- Bottom Left (index 7) --> | |
| <RadioButton Grid.Row="2" Grid.Column="0" GroupName="ToolbarPosition" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=7}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Bottom left corner" | |
| ToolTipService.ToolTip="Bottom left corner"/> | |
| <!-- Bottom Center (index 1) --> | |
| <RadioButton Grid.Row="2" Grid.Column="1" GroupName="ToolbarPosition" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=1}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Bottom center" | |
| ToolTipService.ToolTip="Bottom center"/> | |
| <!-- Bottom Right (index 6) --> | |
| <RadioButton Grid.Row="2" Grid.Column="2" GroupName="ToolbarPosition" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=6}" | |
| Style="{StaticResource PositionRadioButtonStyle}" | |
| AutomationProperties.Name="Bottom right corner" | |
| ToolTipService.ToolTip="Bottom right corner"/> | |
| x:Uid="QuickAccent_ToolbarPosition_TopRightCorner" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=4}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> | |
| <!-- Left (index 2) --> | |
| <RadioButton Grid.Row="1" Grid.Column="0" GroupName="ToolbarPosition" | |
| x:Uid="QuickAccent_ToolbarPosition_Left" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=2}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> | |
| <!-- Center (index 8) --> | |
| <RadioButton Grid.Row="1" Grid.Column="1" GroupName="ToolbarPosition" | |
| x:Uid="QuickAccent_ToolbarPosition_Center" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=8}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> | |
| <!-- Right (index 3) --> | |
| <RadioButton Grid.Row="1" Grid.Column="2" GroupName="ToolbarPosition" | |
| x:Uid="QuickAccent_ToolbarPosition_Right" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=3}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> | |
| <!-- Bottom Left (index 7) --> | |
| <RadioButton Grid.Row="2" Grid.Column="0" GroupName="ToolbarPosition" | |
| x:Uid="QuickAccent_ToolbarPosition_BottomLeftCorner" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=7}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> | |
| <!-- Bottom Center (index 1) --> | |
| <RadioButton Grid.Row="2" Grid.Column="1" GroupName="ToolbarPosition" | |
| x:Uid="QuickAccent_ToolbarPosition_BottomCenter" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=1}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> | |
| <!-- Bottom Right (index 6) --> | |
| <RadioButton Grid.Row="2" Grid.Column="2" GroupName="ToolbarPosition" | |
| x:Uid="QuickAccent_ToolbarPosition_BottomRightCorner" | |
| IsChecked="{x:Bind Path=ViewModel.ToolbarPositionIndex, Mode=TwoWay, Converter={StaticResource IndexToBoolConverter}, ConverterParameter=6}" | |
| Style="{StaticResource PositionRadioButtonStyle}"/> |
| /// Converts an integer index to a boolean value for use with RadioButton groups. | ||
| /// The ConverterParameter specifies which index value should return true. | ||
| /// </summary> | ||
| public partial class IndexToBoolConverter : IValueConverter |
Copilot
AI
Dec 6, 2025
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.
[nitpick] Consider marking this class as sealed for consistency with most other converters in the codebase (e.g., UpdateStateToBoolConverter, ImageResizerUnitToIntConverter). The sealed modifier provides minor performance benefits and prevents unnecessary inheritance.
| public partial class IndexToBoolConverter : IValueConverter | |
| public sealed partial class IndexToBoolConverter : IValueConverter |
- Add 'sealed' modifier to IndexToBoolConverter class - Return DependencyProperty.UnsetValue instead of -1 in ConvertBack - Add Disabled visual state with 0.4 opacity for accessibility - Use x:Uid for RadioButton localization instead of hardcoded strings
Summary
Closes #44111
Replaced the ComboBox dropdown for Quick Accent toolbar position with a visual 3×3 grid, making position selection more intuitive.
PR Checklist
Changes
Visual Layout
\
+----+----+----+
| TL | TC | TR |
+----+----+----+
| L | C | R |
+----+----+----+
| BL | BC | BR |
+----+----+----+
\\
Each tile has tooltip for accessibility.