Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2285,7 +2285,7 @@ long windowProc (long hwnd, int msg, long wParam, long lParam) {
if (display.taskBar != null) {
for (TaskItem item : display.taskBar.items) {
if (item != null && item.shell == this) {
item.recreate ();
item.onTaskbarButtonCreated();
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
public class TaskItem extends Item {
TaskBar parent;
Shell shell;
boolean hasTaskbarButton = false;
int progress, progressState = SWT.DEFAULT;
Image overlayImage;
String overlayText = "";
Expand Down Expand Up @@ -89,6 +90,28 @@ protected void checkSubclass () {

@Override
void destroyWidget () {
if (null != parent.mTaskbarList3) { // extra safety just in case
// MSDN for 'ITaskbarList3::SetOverlayIcon' says:
// Because a single overlay is applied to the taskbar button instead
// of to the individual window thumbnails, this is a per-group
// feature rather than per-window. Requests for overlay icons can
// be received from individual windows in a taskbar group, but they
// do not queue. The last overlay received is the overlay shown.
// If the last overlay received is removed, the overlay that it
// replaced is restored so long as it is still active. As an example,
// windows 1, 2, and 3 set, in order, overlays A, B, and C.
// Because overlay C was received last, it is shown on the taskbar button.
// Window 2 calls SetOverlayIcon with a NULL value to remove overlay B.
// Window 3 then does the same to remove overlay C.
// Because window 1's overlay A is still active, that overlay is then
// displayed on the taskbar button.
// This implies that Shell should pass NULL before closing.
//
// Issue #603: On Win11 the lack of this actually caused wrong overlay
// to show after closing Shell.
parent.mTaskbarList3.SetOverlayIcon(shell.handle, 0, 0);
}

parent.destroyItem (this);
releaseHandle ();
}
Expand Down Expand Up @@ -188,16 +211,27 @@ public int getProgressState () {
return progressState;
}

void recreate () {
if (showingText) {
if (overlayText.length () != 0) updateText ();
void updateImageAndText () {
if (showingText && (overlayText.length () != 0)) {
updateText ();
} else if (overlayImage != null) {
updateImage ();
} else {
if (overlayImage != null) updateImage ();
parent.mTaskbarList3.SetOverlayIcon(shell.handle, 0, 0);
}
}

void updateAll () {
updateImageAndText ();
if (progress != 0) updateProgress ();
if (progressState != SWT.DEFAULT) updateProgressState ();
}

void onTaskbarButtonCreated () {
this.hasTaskbarButton = true;
updateAll ();
}

@Override
void releaseHandle () {
super.releaseHandle ();
Expand Down Expand Up @@ -287,15 +321,14 @@ public void setOverlayImage (Image overlayImage) {
if (overlayImage != null && overlayImage.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
if (shell == null) return;
this.overlayImage = overlayImage;
if (overlayImage != null) {
updateImage ();
} else {
if (overlayText.length () != 0) {
updateText ();
} else {
parent.mTaskbarList3.SetOverlayIcon(shell.handle, 0, 0);
}
}
this.showingText = (this.overlayImage == null);

// MSDN for 'ITaskbarList3' says:
// TaskbarButtonCreated ... message must be received by your application before it calls any ITaskbarList3 method
// #updateAll() will be called later when message is received.
if (!hasTaskbarButton) return;

updateImageAndText ();
}

/**
Expand Down Expand Up @@ -329,15 +362,14 @@ public void setOverlayText (String overlayText) {
if (overlayText == null) error (SWT.ERROR_NULL_ARGUMENT);
if (shell == null) return;
this.overlayText = overlayText;
if (overlayText.length () != 0) {
updateText ();
} else {
if (overlayImage != null) {
updateImage ();
} else {
parent.mTaskbarList3.SetOverlayIcon(shell.handle, 0, 0);
}
}
this.showingText = (this.overlayText.length() != 0);

// MSDN for 'ITaskbarList3' says:
// TaskbarButtonCreated ... message must be received by your application before it calls any ITaskbarList3 method
// #updateAll() will be called later when message is received.
if (!hasTaskbarButton) return;

updateImageAndText ();
}

/**
Expand Down Expand Up @@ -371,6 +403,12 @@ public void setProgress (int progress) {
progress = Math.max (0, Math.min (progress, PROGRESS_MAX));
if (this.progress == progress) return;
this.progress = progress;

// MSDN for 'ITaskbarList3' says:
// TaskbarButtonCreated ... message must be received by your application before it calls any ITaskbarList3 method
// #updateAll() will be called later when message is received.
if (!hasTaskbarButton) return;

updateProgress ();
}

Expand Down Expand Up @@ -414,6 +452,12 @@ public void setProgressState (int progressState) {
if (shell == null) return;
if (this.progressState == progressState) return;
this.progressState = progressState;

// MSDN for 'ITaskbarList3' says:
// TaskbarButtonCreated ... message must be received by your application before it calls any ITaskbarList3 method
// #updateAll() will be called later when message is received.
if (!hasTaskbarButton) return;

updateProgressState ();
}

Expand All @@ -429,7 +473,6 @@ public void handleEvent (Event event) {
}

void updateImage () {
showingText = false;
Image image2 = null;
long hIcon = 0;
switch (overlayImage.type) {
Expand Down Expand Up @@ -463,8 +506,7 @@ void updateProgressState () {
parent.mTaskbarList3.SetProgressState(shell.handle, tbpFlags);
}

void updateText () {
showingText = true;
long renderTextIcon () {
/* Create resources */
int width = 16, height = 16;
long hdc = OS.GetDC (0);
Expand Down Expand Up @@ -547,6 +589,11 @@ void updateText () {
OS.DeleteObject (hBitmap);
OS.DeleteObject (hMask);

return hIcon;
}

void updateText () {
long hIcon = renderTextIcon();
parent.mTaskbarList3.SetOverlayIcon(shell.handle, hIcon, 0);
OS.DestroyIcon (hIcon);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (c) 2023 Syntevo and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Syntevo - initial API and implementation
*******************************************************************************/

package org.eclipse.swt.tests.manual;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public final class Issue0603_WrongTaskBarOverlays {
public static void main(String[] args) {
final Display display = new Display();

final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, true));

final Label hint = new Label(shell, 0);
hint.setText(
"1. Use Win11; Win10 is not enough\n" +
"2. Start the program and pin its icon on TaskBar\n" +
"3. Press the button below to open a few Shells with overlays\n" +
"4. Issue #603: when one of them is closed, it doesn't restore previous overlay correctly\n" +
" Note that Windows is documented to restore the overlay that was set last, contrary to\n" +
" overlay of currently active Shell\n" +
"5. Issue #603: when all of them is closed, there should be no overlay\n" +
"6. The checkboxes below are to test that patch didn't break setting text/image overlays"
);

Button button = new Button(shell, SWT.PUSH);
button.setText("Test");
button.addListener(SWT.Selection, e -> {
for (int iShell = 0; iShell < 3; iShell++) {
Shell shell2 = new Shell(display);
shell2.setText("Shell #" + iShell);
shell2.setSize(250, 100);
shell2.open();

final TaskItem taskItem2 = display.getSystemTaskBar().getItem(shell2);
taskItem2.setOverlayText("#" + iShell);

// Windows Explorer seems to get confused when multiple overlays are set quickly
// It will then sometimes put them in wrong order.
// The workaround is to wait and process display events after each shell
{
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}

while (display.readAndDispatch()) {
}
}
}
});

final TaskItem taskItem = display.getSystemTaskBar().getItem(shell);

Button chkText = new Button(shell, SWT.CHECK);
chkText.setText("Set Text overlay for me");
chkText.addListener(SWT.Selection, e -> {
if (chkText.getSelection()) {
taskItem.setOverlayText("Main");
} else {
taskItem.setOverlayText("");
}
});

Image image = new Image(display, 16, 16);
{
GC gc = new GC(image);
gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
gc.fillRectangle(0, 0, 16, 16);
gc.dispose();
}

Button chkImage = new Button(shell, SWT.CHECK);
chkImage.setText("Set Image overlay for me");
chkImage.addListener(SWT.Selection, e -> {
if (chkImage.getSelection()) {
taskItem.setOverlayImage(image);
} else {
taskItem.setOverlayImage(null);
}
});

shell.pack();
shell.open();

while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}

image.dispose();
}
}