Skip to content

Add taskbar progress and state support for Windows & macOS #106560

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

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

Conversation

SheepYhangCN
Copy link
Contributor

@SheepYhangCN SheepYhangCN commented May 18, 2025

Use ITaskbarList3 for Windows
Use NSView for macOS (by @bruvzg )

Fixes godotengine/godot-proposals#2414

Copy link
Contributor

@RedMser RedMser left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, this is a feature I was personally missing in Godot!
While I realize the PR is still in draft, here's a few things I noticed.

Also you probably saw CI is failing because other display servers still need the methods implemented (even if they do nothing).

@bruvzg bruvzg self-requested a review May 18, 2025 20:44
Copy link
Member

@bruvzg bruvzg left a comment

Choose a reason for hiding this comment

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

macOS does not have a default progress bar for the dock, but arbitrary control can be added to the icon, so it is possible to replicate the same behavior. Not sure about other platforms, probably not supported.

@SheepYhangCN
Copy link
Contributor Author

macOS does not have a default progress bar for the dock, but arbitrary control can be added to the icon, so it is possible to replicate the same behavior. Not sure about other platforms, probably not supported.

I have no mac device and I don't have any experience on macOS app development
I don't know how to do and I also can't test
Sooooo this pr only for Windows I guess

@SheepYhangCN SheepYhangCN force-pushed the taskbar-progress-state branch from 4f9dd3f to 618f7cb Compare May 19, 2025 10:59
@SheepYhangCN SheepYhangCN marked this pull request as ready for review May 19, 2025 12:24
@SheepYhangCN SheepYhangCN requested review from a team as code owners May 19, 2025 12:24
@SheepYhangCN SheepYhangCN requested review from a team as code owners May 19, 2025 12:24
@m4gr3d
Copy link
Contributor

m4gr3d commented May 19, 2025

A taskbar is not supported on all platforms we support, so instead we - should change the terminology to notification; on Windows it'll be via the taskbar, on other platforms the appropriate mechanism will be used instead.

For example, window_set_taskbar_progress_state should become window_notify_progress_state.

@SheepYhangCN
Copy link
Contributor Author

A taskbar is not supported on all platforms we support, so instead we - should change the terminology to notification; on Windows it'll be via the taskbar, on other platforms the appropriate mechanism will be used instead.

The progress bar on taskbar is what I want to implement
Not only a progress bar for current window
This is an addition of request_attention, not a new feature.

Besides, I don't know is there another visual of progress bar in another OS
It will be better to implement that as another feature.

@bruvzg
Copy link
Member

bruvzg commented May 20, 2025

A taskbar is not supported on all platforms we support, so instead we - should change the terminology to notification; on Windows it'll be via the taskbar, on other platforms the appropriate mechanism will be used instead.

I do not think it should be mixed with notifications (which, unlike this, can be implemented on all platforms), it's a different feature.

@bruvzg
Copy link
Member

bruvzg commented May 20, 2025

For the reference, it is implementable on macOS, and Linux (not standard, but seems to be supported by multiple DEs - https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry).

@bruvzg
Copy link
Member

bruvzg commented May 20, 2025

A quick POC for macOS implementation (style of process bar is questionable, but in case of macOS anything can be drawn):

Patch
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 8d76b4b8ad..89790be158 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -66,6 +66,7 @@
 @class GodotWindowDelegate;
 @class GodotButtonView;
 @class GodotEmbeddedView;
+@class GodotProgressView;
 @class CALayerHost;
 
 #undef BitMap
@@ -150,6 +151,8 @@ public:
 	List<WindowID> popup_list;
 	uint64_t time_since_popup = 0;
 
+	GodotProgressView *dock_progress = nullptr;
+
 private:
 #if defined(GLES3_ENABLED)
 	GLManagerLegacy_MacOS *gl_manager_legacy = nullptr;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index d1f9d95298..be2ac7b130 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -2902,12 +2902,98 @@ void DisplayServerMacOS::window_request_attention(WindowID p_window) {
 	[NSApp requestUserAttention:NSCriticalRequest];
 }
 
+@interface GodotProgressView : NSView {
+	DisplayServer::ProgressState pr_state;
+	float pr_value;
+	float pr_offset;
+}
+- (void)setValue:(float)value;
+- (void)setState:(DisplayServer::ProgressState)state;
+@end
+
+@implementation GodotProgressView
+
+- (id)init {
+	self = [super init];
+	pr_state = DisplayServer::PROGRESS_STATE_NOPROGRESS;
+	pr_value = 0.f;
+	pr_offset = 0.f;
+	return self;
+}
+
+- (void)setValue:(float)value {
+	pr_value = value;
+}
+
+- (void)setState:(DisplayServer::ProgressState)state {
+	pr_state = state;
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+	// Icon draw.
+	[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
+	[[NSApp applicationIconImage] drawInRect:self.bounds];
+
+	if (pr_state == DisplayServer::PROGRESS_STATE_NOPROGRESS) {
+		return;
+	}
+
+	// Border draw.
+	NSRect rect = NSMakeRect(1.f, 1.f, self.bounds.size.width - 2.f, 16.f);
+	NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:8.f yRadius:8.f];
+	[bezier_path setLineWidth:2.0];
+	[[NSColor grayColor] set];
+	[bezier_path stroke];
+
+	// Fill clip path.
+	rect = NSMakeRect(2.f, 2.f, self.bounds.size.width - 4.f, 14.f);
+	bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:7.f yRadius:7.f];
+	[bezier_path setLineWidth:1.0];
+	[bezier_path addClip];
+
+	// Fill draw.
+	if (pr_state == DisplayServer::PROGRESS_STATE_INDETERMINATE) {
+		rect.size.width /= 5.0;
+		pr_offset += rect.size.width / 10.0;
+		if (pr_offset > self.bounds.size.width - rect.size.width) {
+			pr_offset = 0.f;
+		}
+		rect.origin.x += pr_offset;
+	} else {
+		rect.size.width = floor(rect.size.width * pr_value);
+	}
+	if (pr_state == DisplayServer::PROGRESS_STATE_ERROR) {
+		[[NSColor colorWithSRGBRed:1.0 green:0.2 blue:0.2 alpha:1.0] set];
+	} else if (pr_state == DisplayServer::PROGRESS_STATE_PAUSED) {
+		[[NSColor colorWithSRGBRed:1.0 green:1.0 blue:0.2 alpha:1.0] set];
+	} else {
+		[[NSColor colorWithSRGBRed:0.2 green:0.6 blue:1.0 alpha:1.0] set];
+	}
+	NSRectFill(rect);
+}
+
+@end
+
 void DisplayServerMacOS::window_set_taskbar_progress_value(float p_value, WindowID p_window) {
-	// Not supported.
+	ERR_FAIL_COND(p_window != MAIN_WINDOW_ID);
+
+	if (!dock_progress) {
+		dock_progress = [[GodotProgressView alloc] init];
+		[NSApp.dockTile setContentView:dock_progress];
+	}
+
+	[dock_progress setValue:p_value];
 }
 
 void DisplayServerMacOS::window_set_taskbar_progress_state(ProgressState p_state, WindowID p_window) {
-	// Not supported.
+	ERR_FAIL_COND(p_window != MAIN_WINDOW_ID);
+
+	if (!dock_progress) {
+		dock_progress = [[GodotProgressView alloc] init];
+		[NSApp.dockTile setContentView:dock_progress];
+	}
+
+	[dock_progress setState:p_state];
 }
 
 void DisplayServerMacOS::window_move_to_foreground(WindowID p_window) {
@@ -3511,6 +3597,11 @@ void DisplayServerMacOS::_process_events(bool p_pump) {
 		}
 	}
 
+	if (dock_progress) {
+		dock_progress.needsDisplay = true;
+		[NSApp.dockTile display];
+	}
+
 	_THREAD_SAFE_UNLOCK_
 }
 
Screen.Recording.2025-05-20.at.11.43.41.mov

@SheepYhangCN
Copy link
Contributor Author

A quick POC for macOS implementation

Thank you very much!
I will add it later

For the reference, it is implementable on macOS, and Linux (not standard, but seems to be supported by multiple DEs - https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry).

I am looking into this for Linux support
How can I get app_uri?

@bruvzg
Copy link
Member

bruvzg commented May 20, 2025

How can I get app_uri?

I do not think you can, there's no standard .desktop file for Godot, so if it depends on it, this API might be unusable.

Co-Authored-By: bruvzg <7645683+bruvzg@users.noreply.github.com>
@SheepYhangCN SheepYhangCN changed the title Add taskbar progress and state support for Windows Add taskbar progress and state support for Windows & macOS May 20, 2025
@bruvzg bruvzg self-requested a review May 20, 2025 17:02
SheepYhangCN and others added 3 commits May 21, 2025 17:56
Co-authored-by: bruvzg <7645683+bruvzg@users.noreply.github.com>
Co-authored-by: bruvzg <7645683+bruvzg@users.noreply.github.com>
Co-Authored-By: bruvzg <7645683+bruvzg@users.noreply.github.com>
@SheepYhangCN SheepYhangCN force-pushed the taskbar-progress-state branch from b4b1693 to 10fe2b9 Compare May 21, 2025 12:07
@SheepYhangCN SheepYhangCN requested a review from a team as a code owner May 21, 2025 12:07
@SheepYhangCN
Copy link
Contributor Author

All done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for the OS task bar progress API
6 participants