Skip to content

Commit 0592bbe

Browse files
committed
feat(widget): add a custom left margin for the taskbar widget
The widget position was always derived from the position setting plus the automatic padding (which snaps it next to the native Widgets button), so there was no way to place it at an exact distance from the start of the taskbar. - new "TaskbarWidgetUseCustomLeftMargin" toggle and "TaskbarWidgetLeftMargin" value (0-2000 px, default 20) with a numeric text box - with the position set to "near start" the widget's left edge (top edge on a vertical taskbar) is placed at exactly that distance, ignoring the automatic padding; the existing manual padding still adds an offset on top - the visualizer is moved to the right of the widget when there is not enough room on the left, so a small margin can no longer push it off the taskbar Disabled by default, so nothing changes unless the toggle is turned on.
1 parent 4947f69 commit 0592bbe

4 files changed

Lines changed: 103 additions & 12 deletions

File tree

‎FluentFlyoutWPF/Pages/TaskbarWidgetPage.xaml‎

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<RowDefinition Height="Auto" />
3636
<RowDefinition Height="Auto" />
3737
<RowDefinition Height="Auto" />
38+
<RowDefinition Height="Auto" />
3839
</Grid.RowDefinitions>
3940
<StackPanel Orientation="Horizontal" Margin="0,0,0,24">
4041
<Border CornerRadius="10" ClipToBounds="True" Width="164" Height="98">
@@ -116,7 +117,23 @@
116117
<ComboBoxItem Content="{DynamicResource PositionBottomRight}" />
117118
</ComboBox>
118119
</ui:CardControl>
119-
<ui:CardControl x:Name="TaskbarWidgetSelectedMonitorTitleCard" Tag="Indexable" Grid.Row="5" Icon="{ui:SymbolIcon ProjectionScreen24}" Margin="0,0,0,3">
120+
<ui:CardControl x:Name="TaskbarWidgetLeftMarginTitleCard" Tag="Indexable" Grid.Row="5" Icon="{ui:SymbolIcon PaddingLeft24}" Margin="0,0,0,3">
121+
<ui:CardControl.Header>
122+
<StackPanel Orientation="Vertical">
123+
<TextBlock Text="{DynamicResource TaskbarWidgetLeftMarginTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
124+
<TextBlock Text="{DynamicResource TaskbarWidgetLeftMarginDescription}" FontSize="12" Opacity="0.5" />
125+
</StackPanel>
126+
</ui:CardControl.Header>
127+
<StackPanel Orientation="Horizontal">
128+
<ui:TextBox Width="60" ClearButtonEnabled="False" Text="{Binding TaskbarWidgetLeftMarginText, Mode=TwoWay}"
129+
IsEnabled="{Binding TaskbarWidgetUseCustomLeftMargin}" />
130+
<TextBlock Text="{DynamicResource PixelsUnit}" FontSize="14" FontWeight="Regular" Margin="10,0,0,0" VerticalAlignment="Center"
131+
IsEnabled="{Binding TaskbarWidgetUseCustomLeftMargin}" />
132+
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetUseCustomLeftMargin, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" Margin="16,0,0,0" />
133+
</StackPanel>
134+
</ui:CardControl>
135+
136+
<ui:CardControl x:Name="TaskbarWidgetSelectedMonitorTitleCard" Tag="Indexable" Grid.Row="6" Icon="{ui:SymbolIcon ProjectionScreen24}" Margin="0,0,0,3">
120137
<ui:CardControl.Header>
121138
<StackPanel Orientation="Vertical">
122139
<TextBlock Text="{DynamicResource TaskbarWidgetSelectedMonitorTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -127,7 +144,7 @@
127144
</ui:CardControl>
128145

129146
<!-- Padding settings -->
130-
<ui:CardExpander x:Name="TaskbarWidgetPaddingSettingsCard" Tag="Indexable" Grid.Row="6" Icon="{ui:SymbolIcon Wrench24}" ContentPadding="0" Margin="0,0,0,12">
147+
<ui:CardExpander x:Name="TaskbarWidgetPaddingSettingsCard" Tag="Indexable" Grid.Row="7" Icon="{ui:SymbolIcon Wrench24}" ContentPadding="0" Margin="0,0,0,12">
131148
<ui:CardExpander.Header>
132149
<StackPanel Orientation="Vertical">
133150
<TextBlock Text="{DynamicResource TaskbarWidgetPaddingSettingsTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -181,7 +198,7 @@
181198
</StackPanel>
182199
</ui:CardExpander>
183200

184-
<ui:CardControl x:Name="TaskbarWidgetBackgroundBlurTitleCard" Tag="Indexable" Grid.Row="7" Icon="{ui:SymbolIcon ColorBackground24}" Margin="0,0,0,3">
201+
<ui:CardControl x:Name="TaskbarWidgetBackgroundBlurTitleCard" Tag="Indexable" Grid.Row="8" Icon="{ui:SymbolIcon ColorBackground24}" Margin="0,0,0,3">
185202
<ui:CardControl.Header>
186203
<StackPanel Orientation="Vertical">
187204
<TextBlock Text="{DynamicResource TaskbarWidgetBackgroundBlurTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -190,7 +207,7 @@
190207
</ui:CardControl.Header>
191208
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetBackgroundBlur, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" />
192209
</ui:CardControl>
193-
<ui:CardControl x:Name="TaskbarWidgetHideCompletelyTitleCard" Tag="Indexable" Grid.Row="8" Icon="{ui:SymbolIcon EyeOff24}" Margin="0,0,0,3">
210+
<ui:CardControl x:Name="TaskbarWidgetHideCompletelyTitleCard" Tag="Indexable" Grid.Row="9" Icon="{ui:SymbolIcon EyeOff24}" Margin="0,0,0,3">
194211
<ui:CardControl.Header>
195212
<StackPanel Orientation="Vertical">
196213
<TextBlock Text="{DynamicResource TaskbarWidgetHideCompletelyTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -199,7 +216,7 @@
199216
</ui:CardControl.Header>
200217
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetHideCompletely, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" />
201218
</ui:CardControl>
202-
<ui:CardControl x:Name="TaskbarWidgetFixedWidthTitleCard" Tag="Indexable" Grid.Row="9" Icon="{ui:SymbolIcon ArrowAutofitWidth24}" Margin="0,0,0,3">
219+
<ui:CardControl x:Name="TaskbarWidgetFixedWidthTitleCard" Tag="Indexable" Grid.Row="10" Icon="{ui:SymbolIcon ArrowAutofitWidth24}" Margin="0,0,0,3">
203220
<ui:CardControl.Header>
204221
<StackPanel Orientation="Vertical">
205222
<StackPanel Orientation="Horizontal">
@@ -210,7 +227,7 @@
210227
</ui:CardControl.Header>
211228
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetFixedWidth, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" />
212229
</ui:CardControl>
213-
<ui:CardControl x:Name="TaskbarWidgetAutoHideTitleCard" Tag="Indexable" Grid.Row="10" Icon="{ui:SymbolIcon EyeLines24}" Margin="0,0,0,12">
230+
<ui:CardControl x:Name="TaskbarWidgetAutoHideTitleCard" Tag="Indexable" Grid.Row="11" Icon="{ui:SymbolIcon EyeLines24}" Margin="0,0,0,12">
214231
<ui:CardControl.Header>
215232
<StackPanel Orientation="Vertical">
216233
<TextBlock Text="{DynamicResource TaskbarWidgetAutoHideTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -219,7 +236,7 @@
219236
</ui:CardControl.Header>
220237
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetAutoHide, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" />
221238
</ui:CardControl>
222-
<ui:CardControl x:Name="TaskbarWidgetControlsTitleCard" Tag="Indexable" Grid.Row="11" Icon="{ui:SymbolIcon VideoPlayPause24}" Margin="0,0,0,3">
239+
<ui:CardControl x:Name="TaskbarWidgetControlsTitleCard" Tag="Indexable" Grid.Row="12" Icon="{ui:SymbolIcon VideoPlayPause24}" Margin="0,0,0,3">
223240
<ui:CardControl.Header>
224241
<StackPanel Orientation="Vertical">
225242
<TextBlock Text="{DynamicResource TaskbarWidgetControlsTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -228,7 +245,7 @@
228245
</ui:CardControl.Header>
229246
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetControlsEnabled, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" />
230247
</ui:CardControl>
231-
<ui:CardControl x:Name="TaskbarWidgetControlsPositionTitleCard" Tag="Indexable" Grid.Row="12" Icon="{ui:SymbolIcon ArrowMove24}" Margin="0,0,0,12">
248+
<ui:CardControl x:Name="TaskbarWidgetControlsPositionTitleCard" Tag="Indexable" Grid.Row="13" Icon="{ui:SymbolIcon ArrowMove24}" Margin="0,0,0,12">
232249
<ui:CardControl.Header>
233250
<StackPanel Orientation="Vertical">
234251
<TextBlock Text="{DynamicResource TaskbarWidgetControlsPositionTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -241,7 +258,7 @@
241258
</ComboBox>
242259
</ui:CardControl>
243260

244-
<ui:CardControl x:Name="TaskbarWidgetScrollVolumeTitleCard" Tag="Indexable" Grid.Row="15" Icon="{ui:SymbolIcon Speaker224}" Margin="0,0,0,3">
261+
<ui:CardControl x:Name="TaskbarWidgetScrollVolumeTitleCard" Tag="Indexable" Grid.Row="16" Icon="{ui:SymbolIcon Speaker224}" Margin="0,0,0,3">
245262
<ui:CardControl.Header>
246263
<StackPanel Orientation="Vertical">
247264
<StackPanel Orientation="Horizontal">
@@ -258,7 +275,7 @@
258275
</ComboBox>
259276
</ui:CardControl>
260277

261-
<ui:CardExpander x:Name="TaskbarWidgetScrollingTextCard" Tag="Indexable" Grid.Row="14" Icon="{ui:SymbolIcon TextDirectionHorizontalRight24}" ContentPadding="0" Margin="0,0,0,12">
278+
<ui:CardExpander x:Name="TaskbarWidgetScrollingTextCard" Tag="Indexable" Grid.Row="15" Icon="{ui:SymbolIcon TextDirectionHorizontalRight24}" ContentPadding="0" Margin="0,0,0,12">
262279
<ui:CardExpander.Header>
263280
<Grid ColumnDefinitions="*, Auto">
264281
<StackPanel Orientation="Vertical">
@@ -295,7 +312,7 @@
295312
</StackPanel>
296313
</ui:CardExpander>
297314

298-
<ui:CardControl x:Name="TaskbarWidgetAnimatedTitleCard" Tag="Indexable" Grid.Row="13" Icon="{ui:SymbolIcon Wand24}" Margin="0,0,0,3">
315+
<ui:CardControl x:Name="TaskbarWidgetAnimatedTitleCard" Tag="Indexable" Grid.Row="14" Icon="{ui:SymbolIcon Wand24}" Margin="0,0,0,3">
299316
<ui:CardControl.Header>
300317
<StackPanel Orientation="Vertical">
301318
<TextBlock Text="{DynamicResource TaskbarWidgetAnimatedTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
@@ -305,7 +322,7 @@
305322
<ui:ToggleSwitch IsChecked="{Binding TaskbarWidgetAnimated, Mode=TwoWay}" IsEnabled="{Binding IsPremiumUnlocked}" />
306323
</ui:CardControl>
307324

308-
<ui:CardControl x:Name="TaskbarWidgetShowPauseOverlayTitleCard" Tag="Indexable" Grid.Row="16" Icon="{ui:SymbolIcon Pause24}" Margin="0,0,0,76">
325+
<ui:CardControl x:Name="TaskbarWidgetShowPauseOverlayTitleCard" Tag="Indexable" Grid.Row="17" Icon="{ui:SymbolIcon Pause24}" Margin="0,0,0,76">
309326
<ui:CardControl.Header>
310327
<StackPanel Orientation="Vertical">
311328
<TextBlock Text="{DynamicResource TaskbarWidgetShowPauseOverlayTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />

‎FluentFlyoutWPF/Resources/Localization/Dictionary-en-US.xaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ hides repeat, shuffle and player info</System:String>
6767
<System:String x:Key="TaskbarWidgetBackgroundBlurDescription">Add background blur effect to the taskbar widget</System:String>
6868
<System:String x:Key="TaskbarWidgetHideCompletelyTitle">Hide Completely When No Media Is Present</System:String>
6969
<System:String x:Key="TaskbarWidgetHideCompletelyDescription">Hides the entire widget when no media is available, instead of displaying a media icon</System:String>
70+
<System:String x:Key="TaskbarWidgetLeftMarginTitle">Left Margin</System:String>
71+
<System:String x:Key="TaskbarWidgetLeftMarginDescription">Distance from the left edge of the taskbar (top edge on a vertical taskbar). Overrides the position and automatic padding settings; the unit is device independent pixels</System:String>
7072
<System:String x:Key="TaskbarWidgetFixedWidthTitle">Fixed Widget Width</System:String>
7173
<System:String x:Key="TaskbarWidgetFixedWidthDescription">Keep the widget at a fixed width instead of resizing to fit the title and artist text</System:String>
7274
<System:String x:Key="TaskbarWidgetAutoHideTitle">Autohide Widget</System:String>

‎FluentFlyoutWPF/ViewModels/UserSettings.cs‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using FluentFlyout.Classes.Settings;
77
using FluentFlyout.Classes.Utils;
88
using FluentFlyout.Controls;
9+
using FluentFlyout.Windows;
910
using FluentFlyoutWPF.Classes;
1011
using FluentFlyoutWPF.Models;
1112
using FluentFlyoutWPF.Windows;
@@ -434,6 +435,43 @@ public string TaskbarWidgetManualPaddingText
434435
[ObservableProperty]
435436
public partial bool TaskbarWidgetFixedWidth { get; set; }
436437

438+
/// <summary>
439+
/// Gets or sets whether the widget is positioned by <see cref="TaskbarWidgetLeftMargin"/> instead of the
440+
/// widget position / automatic padding settings (only used when the position is "near start").
441+
/// </summary>
442+
[ObservableProperty]
443+
public partial bool TaskbarWidgetUseCustomLeftMargin { get; set; }
444+
445+
/// <summary>
446+
/// Distance of the taskbar widget from the start of the taskbar (left edge, or top edge on a vertical
447+
/// taskbar) in device independent pixels, used when <see cref="TaskbarWidgetUseCustomLeftMargin"/> is enabled.
448+
/// </summary>
449+
[ObservableProperty]
450+
[NotifyPropertyChangedFor(nameof(TaskbarWidgetLeftMarginText))]
451+
public partial int TaskbarWidgetLeftMargin { get; set; }
452+
453+
/// <summary>
454+
/// Text representation of <see cref="TaskbarWidgetLeftMargin"/> for the settings text box.
455+
/// </summary>
456+
[XmlIgnore]
457+
public string TaskbarWidgetLeftMarginText
458+
{
459+
get => TaskbarWidgetLeftMargin.ToString();
460+
set
461+
{
462+
if (int.TryParse(value, out var result))
463+
{
464+
TaskbarWidgetLeftMargin = Math.Clamp(result, TaskbarWindow.MinCustomLeftMargin, TaskbarWindow.MaxCustomLeftMargin);
465+
}
466+
else
467+
{
468+
TaskbarWidgetLeftMargin = 20;
469+
}
470+
471+
OnPropertyChanged();
472+
}
473+
}
474+
437475
/// <summary>
438476
/// Gets or sets a value indicating whether the pause icon overlay should be completely hidden from view.
439477
/// </summary>
@@ -752,6 +790,8 @@ public UserSettings()
752790
TaskbarWidgetBackgroundBlur = false;
753791
TaskbarWidgetHideCompletely = false;
754792
TaskbarWidgetFixedWidth = false;
793+
TaskbarWidgetUseCustomLeftMargin = false;
794+
TaskbarWidgetLeftMargin = 20;
755795
TaskbarWidgetShowPauseOverlay = true;
756796
TaskbarWidgetControlsEnabled = false;
757797
TaskbarWidgetControlsPosition = 1;
@@ -926,6 +966,18 @@ partial void OnTaskbarWidgetFixedWidthChanged(bool oldValue, bool newValue)
926966
UpdateTaskbar();
927967
}
928968

969+
partial void OnTaskbarWidgetUseCustomLeftMarginChanged(bool oldValue, bool newValue)
970+
{
971+
if (oldValue == newValue || _initializing) return;
972+
UpdateTaskbar();
973+
}
974+
975+
partial void OnTaskbarWidgetLeftMarginChanged(int oldValue, int newValue)
976+
{
977+
if (oldValue == newValue || _initializing) return;
978+
UpdateTaskbar();
979+
}
980+
929981
partial void OnTaskbarWidgetShowPauseOverlayChanged(bool oldValue, bool newValue)
930982
{
931983
if (oldValue == newValue || _initializing) return;

‎FluentFlyoutWPF/Windows/TaskbarWindow.xaml.cs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public partial class TaskbarWindow : Window
2727

2828
private const double SmallTaskbarDetectionThreshold = 40;
2929

30+
// limits for TaskbarWidgetLeftMargin (device independent pixels)
31+
public const int MinCustomLeftMargin = 0;
32+
public const int MaxCustomLeftMargin = 2000;
33+
3034
private readonly DispatcherTimer _timer;
3135
private readonly int _nativeWidgetsPadding = 216;
3236
private readonly double _scale = 0.9;
@@ -486,6 +490,15 @@ private Rect PositionWidget(IntPtr taskbarHandle, RECT taskbarRect, double dpiSc
486490
switch (SettingsManager.Current.TaskbarWidgetPosition)
487491
{
488492
case 0: // near start (left for horizontal, top for vertical)
493+
// custom left margin: place the widget's left edge (top edge on a vertical taskbar)
494+
// at exactly the configured distance from the start of the taskbar
495+
if (SettingsManager.Current.TaskbarWidgetUseCustomLeftMargin)
496+
{
497+
primaryPos = (int)(Math.Clamp(SettingsManager.Current.TaskbarWidgetLeftMargin,
498+
MinCustomLeftMargin, MaxCustomLeftMargin) * dpiScale);
499+
break;
500+
}
501+
489502
primaryPos = 20;
490503

491504
if (SettingsManager.Current.TaskbarVisualizerEnabled && SettingsManager.Current.TaskbarVisualizerPosition == 0)
@@ -684,6 +697,13 @@ private Rect PositionVisualizer(IntPtr taskbarHandle, RECT taskbarRect, double d
684697
break;
685698
}
686699

700+
// with a small custom left margin the visualizer would be pushed off the start of the taskbar,
701+
// so keep it on screen by placing it after the widget instead
702+
if (primaryPos < 0)
703+
{
704+
primaryPos = (int)(widgetPrimaryStart * dpiScale) + (int)(Widget.Width * dpiScale);
705+
}
706+
687707
// Set visualizer position within canvas
688708
// primaryPos → left (horizontal) or top (vertical); crossPos → top (horizontal) or left (vertical)
689709
Canvas.SetLeft(TaskbarVisualizer, (isVertical ? crossPos : primaryPos) / dpiScale);

0 commit comments

Comments
 (0)