Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
674 changes: 649 additions & 25 deletions api/Avalonia.nupkg.xml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions native/Avalonia.Native/src/OSX/WindowImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ BEGIN_INTERFACE_MAP()

virtual HRESULT GetWindowZOrder (long* zOrder) override;

virtual HRESULT SetDockProgressState (int state) override;

virtual HRESULT SetDockProgressValue (double progress) override;

void EnterFullScreenMode ();

void ExitFullScreenMode ();
Expand Down
66 changes: 66 additions & 0 deletions native/Avalonia.Native/src/OSX/WindowImpl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,72 @@
[zoomButton setEnabled:CanZoom() || (([Window styleMask] & NSWindowStyleMaskFullScreen) != 0 && _isEnabled)];
}

static NSProgressIndicator* s_dockProgressIndicator = nullptr;
static NSView* s_dockContentView = nullptr;

static void EnsureDockProgressIndicator() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDockTile *dockTile = [[NSApplication sharedApplication] dockTile];
NSImageView *iconView = [[NSImageView alloc] init];
[iconView setImage:[NSApplication sharedApplication].applicationIconImage];

NSRect frame = NSMakeRect(0, 0, dockTile.size.width, 15);
s_dockProgressIndicator = [[NSProgressIndicator alloc] initWithFrame:frame];
[s_dockProgressIndicator setStyle:NSProgressIndicatorStyleBar];
[s_dockProgressIndicator setMinValue:0.0];
[s_dockProgressIndicator setMaxValue:1.0];
[s_dockProgressIndicator setDoubleValue:0.0];
[s_dockProgressIndicator setHidden:YES];

s_dockContentView = [[NSView alloc] init];
[s_dockContentView addSubview:iconView];
[s_dockContentView addSubview:s_dockProgressIndicator];

[iconView setFrame:NSMakeRect(0, 0, dockTile.size.width, dockTile.size.height)];

[dockTile setContentView:s_dockContentView];
});
}

HRESULT WindowImpl::SetDockProgressState(int state) {
START_COM_CALL;

@autoreleasepool {
EnsureDockProgressIndicator();

if (state == 0) {
// None
[s_dockProgressIndicator setHidden:YES];
[s_dockProgressIndicator setIndeterminate:NO];
} else if (state == 1) {
// Indeterminate
[s_dockProgressIndicator setHidden:NO];
[s_dockProgressIndicator setIndeterminate:YES];
[s_dockProgressIndicator startAnimation:nil];
} else {
// Normal, Error, Paused - all show the bar
[s_dockProgressIndicator setHidden:NO];
[s_dockProgressIndicator setIndeterminate:NO];
}

[[[NSApplication sharedApplication] dockTile] display];
return S_OK;
}
}

HRESULT WindowImpl::SetDockProgressValue(double progress) {
START_COM_CALL;

@autoreleasepool {
EnsureDockProgressIndicator();

[s_dockProgressIndicator setDoubleValue:progress];
[[[NSApplication sharedApplication] dockTile] display];
return S_OK;
}
}

extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
{
@autoreleasepool
Expand Down
22 changes: 21 additions & 1 deletion samples/Sandbox/MainWindow.axaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Class="Sandbox.MainWindow">
x:Class="Sandbox.MainWindow"
Title="Taskbar Progress Demo"
Width="400" Height="350">
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="Taskbar Progress Demo" FontSize="20" FontWeight="Bold"/>

<TextBlock Text="State:" Margin="0,10,0,0"/>
<ComboBox x:Name="StateCombo" SelectedIndex="0">
<ComboBoxItem Content="None"/>
<ComboBoxItem Content="Indeterminate"/>
<ComboBoxItem Content="Normal"/>
<ComboBoxItem Content="Error"/>
<ComboBoxItem Content="Paused"/>
</ComboBox>

<TextBlock x:Name="ValueLabel" Text="Value: 0%" Margin="0,10,0,0"/>
<Slider x:Name="ProgressSlider" Minimum="0" Maximum="100" Value="0"/>

<Button x:Name="AnimateBtn" Content="Animate 0 -> 100%" Margin="0,10,0,0"/>
<Button x:Name="ResetBtn" Content="Reset"/>
</StackPanel>
</Window>
64 changes: 59 additions & 5 deletions samples/Sandbox/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,71 @@
using Avalonia;
using System;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Input.TextInput;
using Avalonia.Markup.Xaml;
using Avalonia.Win32.WinRT.Composition;
using Avalonia.Threading;

namespace Sandbox
{
public partial class MainWindow : Window
{
private DispatcherTimer? _timer;

public MainWindow()
{
InitializeComponent();

var stateCombo = this.FindControl<ComboBox>("StateCombo")!;
var slider = this.FindControl<Slider>("ProgressSlider")!;
var valueLabel = this.FindControl<TextBlock>("ValueLabel")!;
var animateBtn = this.FindControl<Button>("AnimateBtn")!;
var resetBtn = this.FindControl<Button>("ResetBtn")!;

stateCombo.SelectionChanged += (_, _) =>
{
TaskbarProgressState = stateCombo.SelectedIndex switch
{
0 => Avalonia.Controls.TaskbarProgressState.None,
1 => Avalonia.Controls.TaskbarProgressState.Indeterminate,
2 => Avalonia.Controls.TaskbarProgressState.Normal,
3 => Avalonia.Controls.TaskbarProgressState.Error,
4 => Avalonia.Controls.TaskbarProgressState.Paused,
_ => Avalonia.Controls.TaskbarProgressState.None,
};
};

slider.PropertyChanged += (_, e) =>
{
if (e.Property == Slider.ValueProperty)
{
var pct = slider.Value / 100.0;
TaskbarProgressValue = pct;
valueLabel.Text = $"Value: {slider.Value:F0}%";
}
};

animateBtn.Click += (_, _) =>
{
_timer?.Stop();
slider.Value = 0;
TaskbarProgressState = Avalonia.Controls.TaskbarProgressState.Normal;
stateCombo.SelectedIndex = 2;

_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
_timer.Tick += (_, _) =>
{
slider.Value += 1;
if (slider.Value >= 100)
_timer.Stop();
};
_timer.Start();
};

resetBtn.Click += (_, _) =>
{
_timer?.Stop();
slider.Value = 0;
stateCombo.SelectedIndex = 0;
TaskbarProgressState = Avalonia.Controls.TaskbarProgressState.None;
TaskbarProgressValue = 0;
};
}
}
}
10 changes: 10 additions & 0 deletions src/Avalonia.Controls/Platform/IWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ public interface IWindowImpl : IWindowBaseImpl
/// </summary>
void ShowTaskbarIcon(bool value);

/// <summary>
/// Sets the taskbar progress indicator state for this window.
/// </summary>
void SetTaskbarProgressState(TaskbarProgressState state);

/// <summary>
/// Sets the taskbar progress indicator value for this window.
/// </summary>
void SetTaskbarProgressValue(ulong completed, ulong total);

/// <summary>
/// Enables or disables resizing of the window
/// </summary>
Expand Down
33 changes: 33 additions & 0 deletions src/Avalonia.Controls/TaskbarProgressState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Avalonia.Controls
{
/// <summary>
/// Specifies the state of the progress indicator displayed in the taskbar.
/// </summary>
public enum TaskbarProgressState
{
/// <summary>
/// No progress is displayed.
/// </summary>
None = 0,

/// <summary>
/// Pulsing green indicator is displayed.
/// </summary>
Indeterminate = 1,

/// <summary>
/// Green progress indicator is displayed.
/// </summary>
Normal = 2,

/// <summary>
/// Red progress indicator is displayed.
/// </summary>
Error = 4,

/// <summary>
/// Yellow progress indicator is displayed.
/// </summary>
Paused = 8,
}
}
34 changes: 33 additions & 1 deletion src/Avalonia.Controls/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ public class Window : WindowBase, IFocusScope, ILayoutRoot
public static readonly StyledProperty<bool> ShowInTaskbarProperty =
AvaloniaProperty.Register<Window, bool>(nameof(ShowInTaskbar), true);

/// <summary>
/// Defines the <see cref="TaskbarProgressState"/> property.
/// </summary>
public static readonly StyledProperty<TaskbarProgressState> TaskbarProgressStateProperty =
AvaloniaProperty.Register<Window, TaskbarProgressState>(nameof(TaskbarProgressState), TaskbarProgressState.None);

/// <summary>
/// Defines the <see cref="TaskbarProgressValue"/> property.
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TaskbarProgressValue property doesn't validate that the input is within the expected range of 0.0 to 1.0. While the documentation states this range, accepting values outside this range could lead to unexpected behavior in platform implementations. Consider adding validation to clamp or throw an exception for out-of-range values, or document that values outside this range have undefined behavior.

Suggested change
/// Defines the <see cref="TaskbarProgressValue"/> property.
/// Defines the <see cref="TaskbarProgressValue"/> property.
/// The value is expected to be in the range from 0.0 to 1.0. Values outside this range
/// have undefined behavior and may lead to unexpected results in platform implementations.

Copilot uses AI. Check for mistakes.
/// </summary>
public static readonly StyledProperty<double> TaskbarProgressValueProperty =
AvaloniaProperty.Register<Window, double>(nameof(TaskbarProgressValue), 0.0);
Comment on lines +157 to +164
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new TaskbarProgressState and TaskbarProgressValue properties lack test coverage. Other Window properties like Title have corresponding tests in WindowTests.cs that verify the property values are correctly passed to the platform implementation. Consider adding tests similar to Setting_Title_Should_Set_Impl_Title to verify that these properties correctly invoke SetTaskbarProgressState and SetTaskbarProgressValue on the platform implementation.

Copilot uses AI. Check for mistakes.

/// <summary>
/// Defines the <see cref="ClosingBehavior"/> property.
/// </summary>
Expand Down Expand Up @@ -254,6 +266,8 @@ public Window(IWindowImpl impl)
CreatePlatformImplBinding(CanMinimizeProperty, canMinimize => PlatformImpl!.SetCanMinimize(canMinimize));
CreatePlatformImplBinding(CanMaximizeProperty, canMaximize => PlatformImpl!.SetCanMaximize(canMaximize));
CreatePlatformImplBinding(ShowInTaskbarProperty, show => PlatformImpl!.ShowTaskbarIcon(show));
CreatePlatformImplBinding(TaskbarProgressStateProperty, state => PlatformImpl!.SetTaskbarProgressState(state));
CreatePlatformImplBinding(TaskbarProgressValueProperty, value => PlatformImpl!.SetTaskbarProgressValue((ulong)(Math.Clamp(value, 0.0, 1.0) * 1000), 1000));

CreatePlatformImplBinding(WindowStateProperty, state => PlatformImpl!.WindowState = state);
CreatePlatformImplBinding(ExtendClientAreaToDecorationsHintProperty, hint => PlatformImpl!.SetExtendClientAreaToDecorationsHint(hint));
Expand Down Expand Up @@ -389,13 +403,31 @@ public bool ShowActivated
/// <summary>
/// Enables or disables the taskbar icon
/// </summary>
///
///
public bool ShowInTaskbar
{
get => GetValue(ShowInTaskbarProperty);
set => SetValue(ShowInTaskbarProperty, value);
}

/// <summary>
/// Gets or sets the taskbar progress indicator state for this window.
/// </summary>
public TaskbarProgressState TaskbarProgressState
{
get => GetValue(TaskbarProgressStateProperty);
set => SetValue(TaskbarProgressStateProperty, value);
}

/// <summary>
/// Gets or sets the taskbar progress indicator value (0.0 to 1.0) for this window.
/// </summary>
public double TaskbarProgressValue
{
get => GetValue(TaskbarProgressValueProperty);
set => SetValue(TaskbarProgressValueProperty, value);
}

/// <summary>
/// Gets or sets a value indicating how the <see cref="Closing"/> event behaves in the presence
/// of child windows.
Expand Down
8 changes: 8 additions & 0 deletions src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ public void ShowTaskbarIcon(bool value)
{
}

public void SetTaskbarProgressState(TaskbarProgressState state)
{
}

public void SetTaskbarProgressValue(ulong completed, ulong total)
{
}

public void CanResize(bool value)
{
}
Expand Down
8 changes: 8 additions & 0 deletions src/Avalonia.DesignerSupport/Remote/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ public void ShowTaskbarIcon(bool value)
{
}

public void SetTaskbarProgressState(TaskbarProgressState state)
{
}

public void SetTaskbarProgressValue(ulong completed, ulong total)
{
}

public void CanResize(bool value)
{
}
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

<AdditionalFiles Include="DBusXml/org.kde.StatusNotifierWatcher.xml" DBusGeneratorMode="Proxy" />
<AdditionalFiles Include="DBusXml/org.kde.StatusNotifierItem.xml" DBusGeneratorMode="Handler" />

<AdditionalFiles Include="DBusXml/com.canonical.Unity.LauncherEntry.xml" DBusGeneratorMode="Handler" />
</ItemGroup>

</Project>
Loading