Merge(#1): Add Cocoa Dock icon progress bar implemenetation

This commit is contained in:
Jan Schürkamp 2023-03-03 16:07:15 +01:00 committed by GitHub
commit d173bf1fed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 179 additions and 26 deletions

View File

@ -98,6 +98,7 @@ video tutorials.
- IntellectualKitty
- Aaron Jacobs
- JannikGM
- Andreas O. Jansen
- Erik S. V. Jansson
- jjYBdx4IL
- Toni Jovanoski

View File

@ -1161,7 +1161,7 @@ attention, the system will automatically end the request.
@subsection window_taskbar_progress Window taskbar progress
If you whish to display the progress of some action on the taskbar, you can
If you wish to display the progress of some action on the taskbar, you can
do this with @ref glfwSetWindowTaskbarProgress.
@code

View File

@ -1289,6 +1289,8 @@ extern "C" {
*
* @remark @x11 @wayland This behaves like @ref GLFW_TASKBAR_PROGRESS_NORMAL.
*
* @remark @macos This displays a standard indeterminate `NSProgressIndicator`.
*
* Used by @ref window_taskbar_progress.
*/
#define GLFW_TASKBAR_PROGRESS_INDETERMINATE 1
@ -1305,7 +1307,7 @@ extern "C" {
*
* @remark @win32 This displays a red progress bar with 100% progress.
*
* @remark @x11 @wayland This behaves like @ref GLFW_TASKBAR_PROGRESS_NORMAL.
* @remark @x11 @wayland @macos This behaves like @ref GLFW_TASKBAR_PROGRESS_NORMAL.
*
* Used by @ref window_taskbar_progress.
*/
@ -1316,7 +1318,7 @@ extern "C" {
*
* @remark @win32 This displays a yellow filled progress bar.
*
* @remark @x11 @wayland This behaves like @ref GLFW_TASKBAR_PROGRESS_NORMAL.
* @remark @x11 @wayland @macos This behaves like @ref GLFW_TASKBAR_PROGRESS_NORMAL.
*
* Used by @ref window_taskbar_progress.
*/
@ -3374,7 +3376,8 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i
*
* @remark @win32 On Windows Vista and earlier, this function will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @remark @macos This function will emit @ref GLFW_FEATURE_UNIMPLEMENTED.
* @remark @macos There exists only one Dock icon progress bar, and this
* displays the combined values of all the windows.
*
* @remark @x11 @wayland Requires a valid application desktop file with the same name
* as the compiled executable. Due to limitations in the Unity Launcher API
@ -3391,7 +3394,7 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i
*
* @ingroup window
*/
GLFWAPI void glfwSetWindowTaskbarProgress(GLFWwindow* window, const int progressState, double value);
GLFWAPI void glfwSetWindowTaskbarProgress(GLFWwindow* window, int progressState, double value);
/*! @brief Retrieves the position of the content area of the specified window.
*

View File

@ -649,6 +649,12 @@ int _glfwInitCocoa(void)
void _glfwTerminateCocoa(void)
{
@autoreleasepool {
if (_glfw.ns.dockProgressIndicator.view != nil)
{
[_glfw.ns.dockProgressIndicator.view removeFromSuperview];
[_glfw.ns.dockProgressIndicator.view release];
}
if (_glfw.ns.inputSource)
{

View File

@ -156,6 +156,11 @@ typedef struct _GLFWwindowNS
// since the last cursor motion event was processed
// This is kept to counteract Cocoa doing the same internally
double cursorWarpDeltaX, cursorWarpDeltaY;
struct {
int state;
double value;
} dockProgressIndicator;
} _GLFWwindowNS;
// Cocoa-specific global data
@ -189,6 +194,13 @@ typedef struct _GLFWlibraryNS
PFN_LMGetKbdType GetKbdType;
CFStringRef kPropertyUnicodeKeyLayoutData;
} tis;
struct {
id view;
int windowCount;
int indeterminateCount;
double totalValue;
} dockProgressIndicator;
} _GLFWlibraryNS;
// Cocoa-specific per-monitor data
@ -218,7 +230,7 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window, const _GLFWwndconfig* wndco
void _glfwDestroyWindowCocoa(_GLFWwindow* window);
void _glfwSetWindowTitleCocoa(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconCocoa(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowTaskbarProgressCocoa(_GLFWwindow* window, const int taskbarState, double value);
void _glfwSetWindowTaskbarProgressCocoa(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosCocoa(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeCocoa(_GLFWwindow* window, int* width, int* height);

View File

@ -197,6 +197,83 @@ static NSUInteger translateKeyToModifierFlag(int key)
//
static const NSRange kEmptyRange = { NSNotFound, 0 };
static NSProgressIndicator* createProgressIndicator(const NSDockTile* dockTile)
{
NSView* contentView = [dockTile contentView];
NSProgressIndicator* indicator = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, contentView.frame.size.width, 15.0f)];
[indicator setStyle:NSProgressIndicatorStyleBar];
if (@available(macOS 11.0, *))
{
[indicator setControlSize:NSControlSizeLarge];
}
[indicator setMinValue:0.0f];
[indicator setMaxValue:1.0f];
[indicator sizeToFit];
[contentView addSubview:indicator];
_glfw.ns.dockProgressIndicator.view = indicator;
return indicator;
}
static void setDockProgressIndicator(int progressState, double value)
{
NSProgressIndicator* indicator = _glfw.ns.dockProgressIndicator.view;
NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
if (indicator == nil)
{
if ([dockTile contentView] == nil)
{
NSImageView *iconView = [[NSImageView alloc] init];
[iconView setImage:[[NSApplication sharedApplication] applicationIconImage]];
[dockTile setContentView:iconView];
[iconView release];
}
indicator = createProgressIndicator(dockTile);
}
// ### Switching from INDETERMINATE to NORMAL, PAUSED or ERROR requires 2 invocations in different frames.
// In MacOS 12 (and probably other versions), an indeterminate progress bar is rendered as a normal bar
// with 0.0 progress. So when calling [progressIndicator setIndeterminate:YES], the indicator actually
// sets its doubleValue to 0.0.
// The bug is caused by NSProgressIndicator not immediately updating its value when it's increasing.
// This code illustrates the exact same problem, but this time from NORMAL, PAUSED and ERROR to INDETERMINATE:
//
// if (progressState == GLFW_TASKBAR_PROGRESS_INDETERMINATE)
// [progressIndicator setDoubleValue:0.75];
// else
// [progressIndicator setDoubleValue:0.25];
//
// This is likely a bug in Cocoa.
//
// ### Progress increments are delayed
// What this also means, is that each time the progress increments, the bar's progress will be 1 frame delayed,
// and only updated once a higher or similar value is again set the next frame.
// Workaround for the aforementioned issues. If there's any versions of MacOS where
// this issue is not present, this should be ommitted in those versions.
if ([indicator isIndeterminate] || [indicator doubleValue] < value)
{
[indicator removeFromSuperview];
[indicator release];
indicator = createProgressIndicator(dockTile);
}
[indicator setIndeterminate:progressState == GLFW_TASKBAR_PROGRESS_INDETERMINATE];
[indicator setHidden:progressState == GLFW_TASKBAR_PROGRESS_DISABLED];
[indicator setDoubleValue:value];
[dockTile display];
}
//------------------------------------------------------------------------
// Delegate for window related notifications
@ -986,6 +1063,8 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window,
void _glfwDestroyWindowCocoa(_GLFWwindow* window)
{
@autoreleasepool {
_glfwSetWindowTaskbarProgressCocoa(window, GLFW_TASKBAR_PROGRESS_DISABLED, 0.0);
if (_glfw.ns.disabledCursorWindow == window)
_glfw.ns.disabledCursorWindow = NULL;
@ -1032,10 +1111,60 @@ void _glfwSetWindowIconCocoa(_GLFWwindow* window,
"Cocoa: Regular windows do not have icons on macOS");
}
void _glfwSetWindowTaskbarProgressCocoa(_GLFWwindow* window, const int progressState, double value)
void _glfwSetWindowTaskbarProgressCocoa(_GLFWwindow* window, int progressState, double value)
{
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
"Cocoa: Window taskbar progress is not implemented");
if (progressState == GLFW_TASKBAR_PROGRESS_ERROR || progressState == GLFW_TASKBAR_PROGRESS_PAUSED)
progressState = GLFW_TASKBAR_PROGRESS_NORMAL;
const int oldState = window->ns.dockProgressIndicator.state;
const int state = progressState;
const double oldValue = window->ns.dockProgressIndicator.value;
if (oldState == state)
{
if (state == GLFW_TASKBAR_PROGRESS_DISABLED ||
state == GLFW_TASKBAR_PROGRESS_INDETERMINATE ||
oldValue == value)
return;
}
if (oldState != state)
{
// Reset
if (oldState == GLFW_TASKBAR_PROGRESS_INDETERMINATE)
--_glfw.ns.dockProgressIndicator.indeterminateCount;
if (oldState != GLFW_TASKBAR_PROGRESS_DISABLED)
{
--_glfw.ns.dockProgressIndicator.windowCount;
_glfw.ns.dockProgressIndicator.totalValue -= oldValue;
}
// Set
if (state == GLFW_TASKBAR_PROGRESS_INDETERMINATE)
++_glfw.ns.dockProgressIndicator.indeterminateCount;
if (state != GLFW_TASKBAR_PROGRESS_DISABLED)
{
++_glfw.ns.dockProgressIndicator.windowCount;
_glfw.ns.dockProgressIndicator.totalValue += value;
}
}
else if (state != GLFW_TASKBAR_PROGRESS_DISABLED)
_glfw.ns.dockProgressIndicator.totalValue += (value - oldValue);
if (_glfw.ns.dockProgressIndicator.windowCount > _glfw.ns.dockProgressIndicator.indeterminateCount)
{
const double finalValue = _glfw.ns.dockProgressIndicator.totalValue / _glfw.ns.dockProgressIndicator.windowCount;
setDockProgressIndicator(GLFW_TASKBAR_PROGRESS_NORMAL, finalValue);
}
else if (_glfw.ns.dockProgressIndicator.indeterminateCount > 0)
setDockProgressIndicator(GLFW_TASKBAR_PROGRESS_INDETERMINATE, 0.0f);
else
setDockProgressIndicator(GLFW_TASKBAR_PROGRESS_DISABLED, 0.0f);
window->ns.dockProgressIndicator.state = state;
window->ns.dockProgressIndicator.value = value;
}
void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos)

View File

@ -89,7 +89,7 @@ GLFWbool _glfwCreateWindowNull(_GLFWwindow* window, const _GLFWwndconfig* wndcon
void _glfwDestroyWindowNull(_GLFWwindow* window);
void _glfwSetWindowTitleNull(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowTaskbarProgressNull(_GLFWwindow* window, const int taskbarState, double value);
void _glfwSetWindowTaskbarProgressNull(_GLFWwindow* window, int progressState, double value);
void _glfwSetWindowMonitorNull(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
void _glfwGetWindowPosNull(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosNull(_GLFWwindow* window, int xpos, int ypos);

View File

@ -187,7 +187,7 @@ void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* ima
{
}
void _glfwSetWindowTaskbarProgressNull(_GLFWwindow* window, const int progressState, double value)
void _glfwSetWindowTaskbarProgressNull(_GLFWwindow* window, int progressState, double value)
{
}

View File

@ -618,7 +618,7 @@ GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window, const _GLFWwndconfig* wndco
void _glfwDestroyWindowWin32(_GLFWwindow* window);
void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowTaskbarProgressWin32(_GLFWwindow* window, const int taskbarState, double value);
void _glfwSetWindowTaskbarProgressWin32(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height);

View File

@ -1596,7 +1596,7 @@ void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* im
}
}
void _glfwSetWindowTaskbarProgressWin32(_GLFWwindow* window, const int progressState, double value)
void _glfwSetWindowTaskbarProgressWin32(_GLFWwindow* window, int progressState, double value)
{
HRESULT res = S_OK;
int winProgressState = 0;

View File

@ -558,7 +558,7 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle,
_glfw.platform.setWindowIcon(window, count, images);
}
GLFWAPI void glfwSetWindowTaskbarProgress(GLFWwindow* handle, const int progressState, double value)
GLFWAPI void glfwSetWindowTaskbarProgress(GLFWwindow* handle, int progressState, double value)
{
_GLFWwindow* window = (_GLFWwindow*) handle;

View File

@ -446,7 +446,7 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window, const _GLFWwndconfig* wnd
void _glfwDestroyWindowWayland(_GLFWwindow* window);
void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconWayland(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowTaskbarProgressWayland(_GLFWwindow* window, const int taskbarState, double value);
void _glfwSetWindowTaskbarProgressWayland(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height);

View File

@ -1902,11 +1902,11 @@ void _glfwSetWindowIconWayland(_GLFWwindow* window,
"Wayland: The platform does not support setting the window icon");
}
void _glfwSetWindowTaskbarProgressWayland(_GLFWwindow* window, const int taskbarState, double value)
void _glfwSetWindowTaskbarProgressWayland(_GLFWwindow* window, const int progressState, double value)
{
(void)window;
const dbus_bool_t progressVisible = (taskbarState != GLFW_TASKBAR_PROGRESS_DISABLED);
const dbus_bool_t progressVisible = (progressState != GLFW_TASKBAR_PROGRESS_DISABLED);
_glfwUpdateTaskbarProgressDBusPOSIX(progressVisible, value);
}

View File

@ -905,7 +905,7 @@ GLFWbool _glfwCreateWindowX11(_GLFWwindow* window, const _GLFWwndconfig* wndconf
void _glfwDestroyWindowX11(_GLFWwindow* window);
void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowTaskbarProgressX11(_GLFWwindow* window, const int taskbarState, double value);
void _glfwSetWindowTaskbarProgressX11(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height);

View File

@ -2152,11 +2152,11 @@ void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* imag
XFlush(_glfw.x11.display);
}
void _glfwSetWindowTaskbarProgressX11(_GLFWwindow* window, const int taskbarState, double value)
void _glfwSetWindowTaskbarProgressX11(_GLFWwindow* window, int progressState, double value)
{
(void)window;
const dbus_bool_t progressVisible = (taskbarState != GLFW_TASKBAR_PROGRESS_DISABLED);
const dbus_bool_t progressVisible = (progressState != GLFW_TASKBAR_PROGRESS_DISABLED);
_glfwUpdateTaskbarProgressDBusPOSIX(progressVisible, value);
}

View File

@ -418,20 +418,22 @@ int main(int argc, char** argv)
nk_layout_row_dynamic(nk, 30, 5);
static int state = GLFW_TASKBAR_PROGRESS_DISABLED;
static float progress = 0;
if(nk_button_label(nk, "No progress"))
glfwSetWindowTaskbarProgress(window, GLFW_TASKBAR_PROGRESS_DISABLED, (double)progress);
glfwSetWindowTaskbarProgress(window, state = GLFW_TASKBAR_PROGRESS_DISABLED, (double) progress);
if (nk_button_label(nk, "Indeterminate"))
glfwSetWindowTaskbarProgress(window, GLFW_TASKBAR_PROGRESS_INDETERMINATE, (double)progress);
glfwSetWindowTaskbarProgress(window, state = GLFW_TASKBAR_PROGRESS_INDETERMINATE, (double) progress);
if (nk_button_label(nk, "Normal"))
glfwSetWindowTaskbarProgress(window, GLFW_TASKBAR_PROGRESS_NORMAL, (double)progress);
glfwSetWindowTaskbarProgress(window, state = GLFW_TASKBAR_PROGRESS_NORMAL, (double) progress);
if (nk_button_label(nk, "Error"))
glfwSetWindowTaskbarProgress(window, GLFW_TASKBAR_PROGRESS_ERROR, (double)progress);
glfwSetWindowTaskbarProgress(window, state = GLFW_TASKBAR_PROGRESS_ERROR, (double) progress);
if (nk_button_label(nk, "Paused"))
glfwSetWindowTaskbarProgress(window, GLFW_TASKBAR_PROGRESS_PAUSED, (double)progress);
glfwSetWindowTaskbarProgress(window, state = GLFW_TASKBAR_PROGRESS_PAUSED, (double) progress);
nk_label(nk, "Progress: ", NK_TEXT_ALIGN_LEFT);
nk_slider_float(nk, 0.0f, &progress, 1.0f, 0.05f);
if (nk_slider_float(nk, 0.0f, &progress, 1.0f, 0.05f))
glfwSetWindowTaskbarProgress(window, state, (double) progress);
}
nk_end(nk);