Bug 577129 - [Win11] Button with Arrow style does not display arrows (UP/DOWN/LEFT/RIGHT)

Buttons use arrows from scrollbar.

The problem is that on Win11, default arrow image for scrollbar is empty
because scrollbars no longer show arrows unless pressed/disabled/hovered
with mouse pointer.

Change-Id: I44413614690f92a5111dcf74d345e19667a7c89d
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/191177
Tested-by: Platform Bot <platform-bot@eclipse.org>
Tested-by: Niraj Modi <niraj.modi@in.ibm.com>
Reviewed-by: Niraj Modi <niraj.modi@in.ibm.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java
index 82516a2..592eaed 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java
@@ -1433,6 +1433,71 @@
 	return super.wmNotifyChild (hdr, wParam, lParam);
 }
 
+static int getThemeStateId(int style, boolean pressed, boolean enabled) {
+	int direction = style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT);
+
+	/*
+	 * Feature in Windows.  DrawThemeBackground() does not mirror the drawing.
+	 * The fix is switch left to right and right to left.
+	 */
+	if ((style & SWT.MIRRORED) != 0) {
+		if        (direction == SWT.LEFT) {
+			direction = SWT.RIGHT;
+		} else if (direction == SWT.RIGHT) {
+			direction = SWT.LEFT;
+		}
+	}
+
+	/*
+	 * On Win11, scrollbars no longer show arrows by default.
+	 * Arrows only show up when hot/disabled/pushed.
+	 * The workaround is to use hot image in place of default.
+	 */
+	boolean hot = false;
+	if (OS.WIN32_BUILD >= OS.WIN32_BUILD_WIN11_21H2) {
+		if (!pressed && enabled) {
+			hot = true;
+		}
+	}
+
+	if (hot) {
+		switch (direction) {
+			case SWT.UP:    return OS.ABS_UPHOT;
+			case SWT.DOWN:  return OS.ABS_DOWNHOT;
+			case SWT.LEFT:  return OS.ABS_LEFTHOT;
+			case SWT.RIGHT: return OS.ABS_RIGHTHOT;
+		}
+	}
+
+	if (pressed) {
+		switch (direction) {
+			case SWT.UP:    return OS.ABS_UPPRESSED;
+			case SWT.DOWN:  return OS.ABS_DOWNPRESSED;
+			case SWT.LEFT:  return OS.ABS_LEFTPRESSED;
+			case SWT.RIGHT: return OS.ABS_RIGHTPRESSED;
+		}
+	}
+
+	if (!enabled) {
+		switch (direction) {
+			case SWT.UP:    return OS.ABS_UPDISABLED;
+			case SWT.DOWN:  return OS.ABS_DOWNDISABLED;
+			case SWT.LEFT:  return OS.ABS_LEFTDISABLED;
+			case SWT.RIGHT: return OS.ABS_RIGHTDISABLED;
+		}
+	}
+
+	switch (direction) {
+		case SWT.UP:    return OS.ABS_UPNORMAL;
+		case SWT.DOWN:  return OS.ABS_DOWNNORMAL;
+		case SWT.LEFT:  return OS.ABS_LEFTNORMAL;
+		case SWT.RIGHT: return OS.ABS_RIGHTNORMAL;
+	}
+
+	// Have some sane value if all else fails
+	return OS.ABS_LEFTNORMAL;
+}
+
 @Override
 LRESULT wmDrawChild (long wParam, long lParam) {
 	if ((style & SWT.ARROW) == 0) return super.wmDrawChild (wParam, lParam);
@@ -1441,29 +1506,9 @@
 	RECT rect = new RECT ();
 	OS.SetRect (rect, struct.left, struct.top, struct.right, struct.bottom);
 	if (OS.IsAppThemed ()) {
-		int iStateId = OS.ABS_LEFTNORMAL;
-		switch (style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) {
-			case SWT.UP: iStateId = OS.ABS_UPNORMAL; break;
-			case SWT.DOWN: iStateId = OS.ABS_DOWNNORMAL; break;
-			case SWT.LEFT: iStateId = OS.ABS_LEFTNORMAL; break;
-			case SWT.RIGHT: iStateId = OS.ABS_RIGHTNORMAL; break;
-		}
-		/*
-		* Feature in Windows.  DrawThemeBackground() does not mirror the drawing.
-		* The fix is switch left to right and right to left.
-		*/
-		if ((style & SWT.MIRRORED) != 0) {
-			if ((style & (SWT.LEFT | SWT.RIGHT)) != 0) {
-				iStateId = iStateId == OS.ABS_RIGHTNORMAL ? OS.ABS_LEFTNORMAL : OS.ABS_RIGHTNORMAL;
-			}
-		}
-		/*
-		* NOTE: The normal, hot, pressed and disabled state is
-		* computed relying on the fact that the increment between
-		* the direction states is invariant (always separated by 4).
-		*/
-		if (!getEnabled ()) iStateId += OS.ABS_UPDISABLED - OS.ABS_UPNORMAL;
-		if ((struct.itemState & OS.ODS_SELECTED) != 0) iStateId += OS.ABS_UPPRESSED - OS.ABS_UPNORMAL;
+		boolean pressed = ((struct.itemState & OS.ODS_SELECTED) != 0);
+		boolean enabled = getEnabled ();
+		int iStateId = getThemeStateId(style, pressed, enabled);
 		OS.DrawThemeBackground (display.hScrollBarTheme (), struct.hDC, OS.SBP_ARROWBTN, iStateId, rect, null);
 	} else {
 		int uState = OS.DFCS_SCROLLLEFT;
diff --git a/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Bug577129_Win11_NoButtonArrows.java b/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Bug577129_Win11_NoButtonArrows.java
new file mode 100644
index 0000000..1958c3e
--- /dev/null
+++ b/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Bug577129_Win11_NoButtonArrows.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2022 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.layout.*;
+import org.eclipse.swt.widgets.*;
+
+public final class Bug577129_Win11_NoButtonArrows {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		final Shell shell = new Shell(display);
+		shell.setLayout (new GridLayout (1, true));
+
+		Label hint = new Label (shell, 0);
+		hint.setText (
+			"1) Run on Win11 (Win10 is not enough)\n" +
+			"2) Bug 577129: Enabled buttons don't show arrows"
+		);
+
+		Composite composite = new Composite(shell, 0);
+		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		composite.setLayout(new GridLayout (5, true));
+
+		Button button;
+
+		new Label(composite, 0).setText("");
+		new Label(composite, 0).setText("UP");
+		new Label(composite, 0).setText("DOWN");
+		new Label(composite, 0).setText("LEFT");
+		new Label(composite, 0).setText("RIGHT");
+
+		for (int isEnabled = 0; isEnabled < 2; isEnabled++) {
+			if (isEnabled != 0) {
+				new Label(composite, 0).setText("Enabled");
+			} else {
+				new Label(composite, 0).setText("Disabled");
+			}
+
+			button = new Button(composite, SWT.ARROW | SWT.UP);
+			if (isEnabled == 0) {
+				button.setEnabled(false);
+			}
+
+			button = new Button(composite, SWT.ARROW | SWT.DOWN);
+			if (isEnabled == 0) {
+				button.setEnabled(false);
+			}
+
+			button = new Button(composite, SWT.ARROW | SWT.LEFT);
+			if (isEnabled == 0) {
+				button.setEnabled(false);
+			}
+
+			button = new Button(composite, SWT.ARROW | SWT.RIGHT);
+			if (isEnabled == 0) {
+				button.setEnabled(false);
+			}
+		}
+
+		shell.pack();
+		shell.open();
+
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch()) {
+				display.sleep();
+			}
+		}
+
+		display.dispose();
+	}
+}